Qt系列文章目录

文章目录

前言

Qt 提供了三种读取 XML 文档的方法
Qt中使用QDomDocument和QDomnode来读取xml
内容简介:一.对QDomDocument和QDomnode的理解QDom前缀的都是代表节点类型。所以有,QDomElement代表一个Element节点,而QDomText代表一个Text节点。QDomNode类可以存储任意类型的节点。如果想进一步处理一个节点,首先必须把它转化为正确的数据类型。QDomNode调用toElement()以把它转化成QDomElement,然后调用tagName()来获得元素的标签名称。如果节点不是Element类型,那么toElement()函数就返回一个空QDomElement对对QDomDocument和QDomnode的理解
QDom前缀的都是代表节点类型。所以有,QDomElement代表一个Element节点,而QDomText代表一个Text节点。QDomNode类可以存储任意类型的节点。如果想进一步处理一个节点,首先必须把它转化为正确的数据类型。QDomNode调用toElement()以把它转化成QDomElement,然后调用tagName()来获得元素的标签名称。如果节点不是Element类型,那么toElement()函数就返回一个空QDomElement对象和一个空标签

一、DOM(Document Object Model)

DOM(Document Object Model):将整个 XML 文档读入内存,构建成一个树结构,允许程序在树结构上向前向后移动导航,这是与另外两种方式最大的区别,也就是允许实现多次解析器(对应于前面所说的一次解析器)。DOM 方式带来的问题是需要一次性将整个 XML 文档读入内存,因此会占用很大内存;

二、QXmlStreamReader

QXmlStreamReader:一种快速的基于流的方式访问良格式 XML 文档,特别适合于实现一次解析器(所谓“一次解析器”,可以理解成我们只需读取文档一次,然后像一个遍历器从头到尾一次性处理 XML 文档,期间不会有反复的情况,也就是不会读完第一个标签,然后读第二个,读完第二个又返回去读第一个,这是不允许的);

三、SAX(Simple API for XML)

SAX(Simple API for XML):提供大量虚函数,以事件的形式处理 XML 文档。这种解析办法主要是由于历史原因提出的,为了解决 DOM 的内存占用提出的(在现代计算机上,这个一般已经不是问题了)。

在 Qt4 中,这三种方式都位于 QtXml 模块中。Qt5 则将QXmlStreamReader/QXmlStreamWriter移动到 QtCore 中,QtXml 则标记为“不再维护”,这已经充分表明了 Qt 的官方意向。

至于生成 XML 文档,Qt 同样提供了三种方式:

QXmlStreamWriter,与QXmlStreamReader相对应;
DOM 方式,首先在内存中生成 DOM 树,然后将 DOM 树写入文件。不过,除非我们程序的数据结构中本来就维护着一个 DOM 树,否则,临时生成树再写入肯定比较麻烦;
纯手工生成 XML 文档,显然,这是最复杂的一种方式。
使用QXmlStreamReader是 Qt 中最快最方便的读取 XML 的方法。因为QXmlStreamReader使用了递增式的解析器,适合于在整个 XML 文档中查找给定的标签、读入无法放入内存的大文件以及处理 XML 的自定义数据。

每次QXmlStreamReader的readNext()函数调用,解析器都会读取下一个元素,按照下表中展示的类型进行处理。我们通过表中所列的有关函数即可获得相应的数据值:

xml例子

<?xml version="1.0" encoding="GB2312" ?>
<Project>
<Earth>
<Earth dom="true" dom_brightness="1.000000" />
<ContourMap>
<ColorMaps currentname="" />
</ContourMap>
</Earth>
<Editable>
<Editable editcoverpath="" editimagepath="" />
</Editable>
<Bim />
<Meteorology />
<Environment>
<DateTime day="13" dst="false" hour="15" min="17" month="5" pass="false" sec="9" sync="false" year="2022" zone="8.000000" />
<Atmos cloud_add_density="0.500000" cloud_height="1500.000000" cloudtype="0" fog="false" fogcolor="0.670000 0.790000 0.890000" fogrange="10000.000000" weatherrate="0.000000" weathertype="0" wind="true" winddirection="0.000000" windspeed="10.000000" />
<Light ambient="1.000000" brightness="1.000000" diffuse="1.000000" high="1.000000" high_power="10.000000" night_power="1.000000" sea_power="1.000000" shadow_power="0.000000" space_color_x="0.500000" space_color_y="0.500000" space_color_z="0.500000" />
<Ocean chop="1.300000" choppiness="1.300000" choppy_x="0.000000" choppy_y="0.310000" choppy_z="0.400000" model_visibility="200.000000" ocean_culling="true" reflect_model="false" reflect_terrain="false" reflect_visibility="30000.000000" sea_level="0.000000" sea_mask_x="0.070000" sea_mask_y="0.190000" sea_mask_z="0.300000" sea_visibility="200.000000" sea_x="0.040000" sea_y="0.120000" sea_z="0.200000" windspeed="10.000000" />
<Ground bumpmaprange="6000.000000" cover_visible="true" detail_alpha="0.550000" detail_attenDist="130.000000" detail_maxRange="450.000000" detailrange="6000.000000" ground_visible="false" procedural_texture="false" splat_blend_rate="0.950000" splat_brightness="1.000000" splat_contrast="1.000000" splat_fade_altitude="4000.000000" splat_range="60000.000000" />
<Splat splataltitudefade="4500" splatambientratio="1.000000" splatblendrate="0.850000" splatbrightness="1.000000" splatcontrast="1.000000" splatdiffuseratio="1.000000" splatvisible="false" splatvisiblealtitude="4000" />
<SplatMaterial />
</Environment>
<Region>
<Data dempath="" demtype="0" dompath="荆州热电厂.tif" domtype="0" maxlevel="18" minlevel="6" mode="0" name="荆州热电厂" visible="true" />
</Region>
<Mask />
<Flatten />
<Terrain />
<Surface />
<Path>
<Data followterrain="true" height="0.000000" loop="1" mode="0" name="test" offsetheight="0.000000" speed="10.000000" type="1" visible="true">
<Points count="7">
<P1 pos="112.230563 31.037060 79.934634" />
<P2 pos="112.229599 31.036415 79.796287" />
<P3 pos="112.229602 31.035366 79.271138" />
<P4 pos="112.231061 31.035135 79.400257" />
<P5 pos="112.231983 31.035851 79.020596" />
<P6 pos="112.231838 31.036912 79.874460" />
<P7 pos="112.230563 31.037065 79.008552" />
</Points>
</Data>
</Path>
<Model>
<Data ent_anisotropy="0" ent_bindname="" ent_bindtype="11" ent_blend="false" ent_castshadow="true" ent_color="1.000000 1.000000 1.000000 -1.000000" ent_density="20.000000" ent_linethick="-1.000000" ent_material="0" ent_offset_pos="0.000000 0.000000 0.000000" ent_ori="0.000000 0.000000 0.000000" ent_ori_type="0" ent_placement="4" ent_polyoffset_factor="0.000000" ent_polyoffset_unit="0.000000" ent_pos="112.232583 31.039165 76.170211" ent_randomheading="false" ent_randomscale="false" ent_randomscalemax="1.000000" ent_randomscalemin="1.000000" ent_reflected="false" ent_renderbin="-1" ent_scale="1.000000 1.000000 1.000000" ent_shape="0" http="false" modelid="0" name="热电厂" path="建筑/redianchang2.ive" range="10000.000000" static="true" type="0" visible="true" />
<Data ent_anisotropy="0" ent_bindname="test" ent_bindtype="0" ent_blend="false" ent_castshadow="true" ent_color="1.000000 1.000000 1.000000 -1.000000" ent_density="20.000000" ent_linethick="-1.000000" ent_material="0" ent_offset_pos="0.000000 0.000000 0.000000" ent_ori="0.000000 0.000000 0.000000" ent_ori_type="0" ent_placement="4" ent_polyoffset_factor="0.000000" ent_polyoffset_unit="0.000000" ent_pos="112.230563 31.037060 79.934634" ent_randomheading="false" ent_randomscale="false" ent_randomscalemax="1.000000" ent_randomscalemin="1.000000" ent_reflected="false" ent_renderbin="-1" ent_scale="1.000000 1.000000 1.000000" ent_shape="0" http="false" modelid="0" name="实体模型019010" path="车辆/T90.ive" range="10000.000000" static="true" type="0" visible="true" />
</Model>
<Road />
<Polygon />
<PolygonPrefab />
<Geometric />
<Vectogram />
<Symbol />
<Image />
<Particle />
<Sound />
<Screen />
<Viewpoint>
<Data day="1" dst="false" heading="-0.000000" height="75.139096" home="false" hour="0" lat="31.037060" lon="112.230563" min="0" month="1" name="荆州热电厂" pitch="-85.500000" range="3446.635041" save_sea_level="false" save_time="false" sea_height="-1000.000000" sea_level="0.000000" sec="0" year="1970" zone="0.000000" />
</Viewpoint>
<Tools />
<Control />
<Effect />
<EffectWater />
<River />
<Simulator />
<ShipWake />
<RotorWash />
<MassiveSymbol />
<Manipulator />
<VehiclePhysx />
<CompositeModel />
<ObliqueModel />
<Aviation />
<Volume />
<Field />
<Decal />
</Project>

Qt中使用QDomDocument和QDomnode来读取xml_解析器

读取节点“Path”属性值

核心代码

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <qfile.h>
#include <qmessagebox.h>
#include <qdebug.h>

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);

setWindowTitle(tr("XML DOM Reader"));

treeWidget = new QTreeWidget(this);
QStringList headers;
headers << "Items" << "Pages";
treeWidget->setHeaderLabels(headers);
setCentralWidget(treeWidget);
}
bool MainWindow::readFile(const QString &fileName)
{
QFile file(fileName);
if (!file.open(QFile::ReadOnly | QFile::Text))
{
QMessageBox::critical(this, tr("Error"),
tr("Cannot read file %1").arg(fileName));
return false;
}

QString errorStr;
int errorLine;
int errorColumn;

QDomDocument doc;
//填充dom树
if (!doc.setContent(&file, false, &errorStr, &errorLine,
&errorColumn))//形参2,是否创建命名空间
{
QMessageBox::critical(this, tr("Error"),
tr("Parse error at line %1, column %2: %3")
.arg(errorLine).arg(errorColumn).arg(errorStr));
return false;
}

QDomElement root = doc.documentElement();//获取dom树的根标签
if (root.tagName() != "Project")
{
QMessageBox::critical(this, tr("Error"),
tr("Not a bookindex file"));
return false;
}
parseBookindexElement(root);
return true;
}
void MainWindow::parseBookindexElement(const QDomElement &element)
{
QDomNode child = element.firstChild();//根标签下的子标签
while (!child.isNull())
{
if (child.toElement().tagName() == "Path")//qdomnode ————》qdomelement的转换基类到子类的转换
{
parseEntryElement(child.toElement(),
treeWidget->invisibleRootItem());
}

if (child.toElement().tagName() == "P1")//qdomnode ————》qdomelement的转换基类到子类的转换
{
parseEntryElement(child.toElement(),
treeWidget->invisibleRootItem());
}

child = child.nextSibling();
}
}

void MainWindow::parseEntryElement(const QDomElement &element,
QTreeWidgetItem *parent)
{
QTreeWidgetItem *item = new QTreeWidgetItem(parent);
QString followterrain = element.attribute("followterrain");
item->setText(0, element.attribute("followterrain"));

QDomNode child = element.firstChild();
QDomNodeList childList = element.childNodes();
while (!child.isNull())//遍历标签的子标签
{
if (child.toElement().tagName() == "Data")
{
parseEntryElement(child.toElement(), item);//递归调用本身
}
else if (child.toElement().tagName() == "height")
{
parsePageElement(child.toElement(), item);
}
else if(child.toElement().tagName() == "Points")
{
QString count = child.toElement().attribute("count");
QDomNodeList childList = child.toElement().childNodes();
int childCount = childList.count();
parsePos(childList);
// parseEntryElement(child.toElement(), item);
// parsePageElement(child.toElement(), item);
}
else if(child.toElement().tagName() == "P1")
{
QString pos = child.toElement().attribute("pos");
// parsePos(child.toElement(), item);
}
child = child.nextSibling();//指针移动一个标签
}
}

void MainWindow::parsePos(QDomNodeList posList)
{
QDomNode item;
QString pos;
QStringList strList;
for(int i=0; i<posList.count(); i++)
{
QDomNode node = posList.at(i);
QDomElement element = node.toElement();
QString strItem = element.tagName();
QString strItemPos = element.attribute("pos");
strList.append(strItemPos);
}
// foreach(posList, item)
// {
// pos = item
// }
}

void MainWindow::parsePageElement(const QDomElement &element,
QTreeWidgetItem *parent)
{
QString page = element.text();
QString allPages = parent->text(1);//最开始的一次为空
qDebug()<<"allPages "<<allPages;
if (!allPages.isEmpty())
{
allPages += ", ";
}
allPages += page;
parent->setText(1, allPages);
}
MainWindow::~MainWindow()
{
delete ui;
}

源码下载

​源码下载地址​