DOM

DOM 是由 W3C 提出的一种处理 XML 文档的标准接口。Qt 实现了 DOM Level 2 级别的不验证读写 XML 文档的方法。DOM 一次性读入整个 XML 文档,在内存中构造为一棵树(被称为 DOM 树)。我们能够在这棵树上进行导航,比如移动到下一节点或者返回上一节点,也可以对这棵树进行修改,或者是直接将这颗树保存为硬盘上的一个 XML 文件。

示例1

头文件

QDomDocument doc;
QStringList PlaySongName;
QString historyName;
QStringList pathList;
QString fileName = "Song.xml";
//写XML
void WriteXml(QStringList &MusicPaths);
//读XML
void readXml(QString& fileName, QDomDocument &XmlDoc);
void addXml(const QString & name, const QString & text, QDomDocument &document);
void AnalysisListData(QDomDocument &xmldoc,QFile &File);
QString DeleteFileNameSpecialCharacters(QString &songName);

void WriteXml(QStringList &MusicPaths);

void MainWindow::WriteXml(QStringList &MusicPaths)
{doc.clear();if (doc.toString().compare("")==0){//创建根节点QDomElement root = doc.createElement("Song");//添加根节点doc.appendChild(root);}
//    QStringList::iterator iter;int k = 0,i = 0;for(i = 0;i < MusicPaths.size();++i){//分离出多个MP3文件路径中每个文件的所有信息QString musicPath = MusicPaths.at(k++);//从MP3文件信息中获取文件名及文件格式信息QString FileName = QFileInfo( musicPath ).fileName();QString songName = DeleteFileNameSpecialCharacters(FileName);addXml(songName,musicPath,doc);//qDebug()<<"i="<<i<<" size="<<MusicPaths.size();}
//    fileName = "Song.xml";QFile file(fileName);if (!file.open(QIODevice::WriteOnly| QIODevice::Text))return ;//将文本流保存到文件,4为子元素缩进的字符数QTextStream out(&file);doc.save(out, 4, QDomNode::EncodingFromTextStream);file.close();readXml(fileName,doc);
}

void readXml(QString& fileName, QDomDocument &XmlDoc);

void MainWindow::readXml(QString &fileName,QDomDocument &XmlDoc)
{QFile file(fileName);qDebug()<<fileName<<" readonly history list";if (!file.open(QIODevice::ReadOnly)){//        qDebug()<<file.errorString();
//        qDebug()<<file.error();
//        qDebug()<<"open file failed";return ;}AnalysisListData(XmlDoc,file);QDomElement root = XmlDoc.documentElement();//读取根节点//读取第一个子节点QDomNode node = root.firstChild();//遍历节点QDomNodeList list = root.childNodes();int count = list.count();qDebug() << "node count:" << count;int i = 0;while (i < count){//获取节点的内容QString data = node.toElement().text();
//        qDebug () << "read == "<<data;m_playList->addMedia(QUrl::fromLocalFile(data));
//        PlaySongName.push_back(QFileInfo( data ).fileName());historyName = data.split("/").last();ui->MusicWidget->addItem(historyName);//读取下一个兄弟节点node = node.nextSibling();node.nextSiblingElement();i++;}file.close();
}

void addXml(const QString & name, const QString & text, QDomDocument &document);

void MainWindow::addXml(const QString &name,const QString &text,QDomDocument &document)
{//读取根节点QDomElement root = document.documentElement();//创建元素节点QDomElement newChild = document.createElement(name);//添加元素节点到根节点root.appendChild(newChild);//创建元素文本QDomText newChildText = document.createTextNode(text);//添加元素文本到元素节点newChild.appendChild(newChildText);
}

void MainWindow::AnalysisListData(QDomDocument &xmldoc, QFile &File)

void MainWindow::AnalysisListData(QDomDocument &xmldoc, QFile &File)
{QString errorStr;int errorLine = 0,errorCol = 0;if(!xmldoc.setContent(&File,true,&errorStr,&errorLine,&errorCol)){//如果出错,则会进入这里。errorStr得到的是出错说明,errorLine和errorCol则是出错的行和列qDebug() << errorStr << "line: " << errorLine << "col: " << errorCol;return;}
}

QString MainWindow::DeleteFileNameSpecialCharacters(QString &songName)

QString MainWindow::DeleteFileNameSpecialCharacters(QString &songName)
{//处理特殊字符等(可自定义)songName = songName.replace(' ',"").replace(':',"");songName = songName.replace('-',"").replace('(',"");songName = songName.replace(')',"").replace('.',"");return songName;
}

示例2

一个汽车销售记录的xml,以时间为顺序进行记录。

xml中用这个作为一个节点 : …

下面给出这个xml的树形结构图:

<?xml version='1.0' encoding='utf-8'?>
<日销售清单><日期 Date="2018--06--17"><时间 time="11--33--22"><工厂>一汽大众</工厂><品牌>奥迪A6</品牌><报价>36</报价><数量>1</数量><金额>36</金额></时间><时间 time="11--33--25"><工厂>一汽大众</工厂><品牌>奔驰</品牌><报价>83</报价><数量>2</数量><金额>166</金额></时间><时间 time="11--33--31"><工厂>二汽神龙</工厂><品牌>毕加索</品牌><报价>39</报价><数量>2</数量><金额>78</金额></时间><时间 time="11--33--35"><工厂>二汽神龙</工厂><品牌>富康</品牌><报价>28</报价><数量>2</数量><金额>56</金额></时间><时间 time="11--33--40"><工厂>二汽神龙</工厂><品牌>标致307</品牌><报价>27</报价><数量>3</数量><金额>81</金额></时间></日期>
</日销售清单>
#include "domxml.h"
#include <QFile>
#include <QDomDocument> //文件
#include <QDomProcessingInstruction> //格式头部
#include <QDomElement>  //元素
#include <QTextStream> //文件流
#include <QDebug>
#include <QDateTime>
#define cout qDebug() << "[" << __FILE__ << ":" << __LINE__ << "]"DomXML::DomXML()
{}//创建xml空文件
void DomXML::createXML(QString filePath)
{QFile file(filePath); //关联文件名字if( true == file.exists() ) //如果存在不创建{cout << "文件已经存在";return;}else{ //不存在才创建//只写方式打开文件bool isOk = file.open(QIODevice::WriteOnly);if(true == isOk)//如果打开成功{//创建xml文档对象QDomDocument doc;//创建xml头部格式 <?xml version='1.0' encoding='utf-8'?>QDomProcessingInstruction ins;ins = doc.createProcessingInstruction("xml", "version=\'1.0\' encoding=\'utf-8\'");//追加元素doc.appendChild(ins);//根节点元素QDomElement root = doc.createElement("日销售清单");doc.appendChild(root);//保存QTextStream stream(&file); //文本流关联文件doc.save(stream, 4); //4 缩进字符file.close(); //关闭文件}else{cout << "WriteOnly error";return;}}}void DomXML::appendXML(QString filePath, QStringList list)
{QFile file(filePath);bool isOk = file.open(QIODevice::ReadOnly);if(true == isOk) //打开成功{//file和xml文档对象关联QDomDocument doc;isOk = doc.setContent(&file);if(isOk) //如果关联成功{file.close(); //关闭文件//获取根节点元素QDomElement root = doc.documentElement();//获取当前时间QDateTime date = QDateTime::currentDateTime();QString dateStr = date.toString("yyyy-MM-dd"); //2016-01-08//判断根节点下有没有子节点if( root.hasChildNodes() ) //如果有子节点{//查找最后一个子节点QDomElement lastEmt = root.lastChildElement();if(lastEmt.attribute("date") == dateStr){//有有当天日期//写有效数据writeXML(doc, lastEmt, list);}else //如果没有{//创建日期子节点元素QDomElement dateEmt = doc.createElement("日期");//创建date属性QDomAttr dateAttr = doc.createAttribute("date");//设置属性的值dateAttr.setNodeValue(dateStr);//节点和属性关联dateEmt.setAttributeNode(dateAttr);//把日期节点追加到根节点上root.appendChild(dateEmt);//写有效数据writeXML(doc, dateEmt, list);}}else //没有子节点{//创建日期子节点元素QDomElement dateEmt = doc.createElement("日期");//创建date属性QDomAttr dateAttr = doc.createAttribute("date");//设置属性的值dateAttr.setNodeValue(dateStr);//节点和属性关联dateEmt.setAttributeNode(dateAttr);//把日期节点追加到根节点上root.appendChild(dateEmt);//写有效数据writeXML(doc, dateEmt, list);}//保存文件isOk = file.open(QIODevice::WriteOnly);if(isOk){QTextStream stream(&file);doc.save(stream, 4);file.close();}}else{cout << "setContent error";return;}}else{cout << "ReadOnly error";}}void DomXML::writeXML(QDomDocument &doc, QDomElement &root, QStringList &list)
{//获取当前时间QDateTime time = QDateTime::currentDateTime();QString timeStr = time.toString("hh-mm-ss"); //16:05:22//创建时间节点元素QDomElement timeEmt = doc.createElement("时间");//创建属性QDomAttr timeAttr = doc.createAttribute("time");//给属性设置值timeAttr.setNodeValue(timeStr);//时间节点元素和属性关联timeEmt.setAttributeNode(timeAttr);//把时间节点追击到日期节点后面root.appendChild(timeEmt);QDomElement factory = doc.createElement("厂家");QDomElement brand = doc.createElement("品牌");QDomElement price = doc.createElement("报价");QDomElement num = doc.createElement("数量");QDomElement total = doc.createElement("金额");QDomText text = doc.createTextNode(list.at(0));factory.appendChild(text);text = doc.createTextNode(list.at(1));brand.appendChild(text);text = doc.createTextNode(list.at(2));price.appendChild(text);text = doc.createTextNode(list.at(3));num.appendChild(text);text = doc.createTextNode(list.at(4));total.appendChild(text);timeEmt.appendChild(factory);timeEmt.appendChild(brand);timeEmt.appendChild(price);timeEmt.appendChild(num);timeEmt.appendChild(total);}void DomXML::readXML(QString filePath, QStringList &fList, QStringList &bList, QStringList &pList, QStringList &nList, QStringList &tList)
{QFile file(filePath);bool isOk = file.open(QIODevice::ReadOnly);if(true == isOk) //打开成功{//file和xml文档对象关联QDomDocument doc;isOk = doc.setContent(&file);if(isOk) //如果关联成功{//获取根节点QDomElement root = doc.documentElement();file.close();QDateTime date = QDateTime::currentDateTime();QString dateStr = date.toString("yyyy-MM-dd");if(root.hasChildNodes()) //有没有子节点{//找最后一个节点元素QDomElement lastEmt = root.lastChildElement();if(lastEmt.attribute("date") == dateStr)//判断有没有当天日期{//找出当前日期下所有时间子节点QDomNodeList list = lastEmt.childNodes();for(int i = 0; i < list.size(); i++){//list.at(0).toElement();//转换为元素,找到时间节点下的所有子节点QDomNodeList subList = list.at(i).toElement().childNodes();//厂家QString factory = subList.at(0).toElement().text();fList.append(factory);QString brand = subList.at(1).toElement().text();bList.append(brand);QString price = subList.at(2).toElement().text();pList.append(price);QString num = subList.at(3).toElement().text();nList.append(num);QString total = subList.at(4).toElement().text();tList.append(total);}}else{cout << "没有当天日期";return;}}else{cout << "没有子节点";return;}}else{cout << "setContent error";return;}}else{cout << "ReadOnly error";return;}}

示例3

下面一个 books.xml 片段:

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

我们可以认为是如下一棵 DOM 树:

Document|--Element(doc)|--Element(quote)|    |--Text("Scio me nihil scire")|--Element(translation)|--Text("I know that I know nothing")

面所示的 DOM 树包含了不同类型的节点。例如,Element 类型的节点有一个开始标签和对应的一个结束标签。在开始标签和结束标签之间的内容作为这个 Element 节点的子节点。在 Qt 中,所有 DOM 节点的类型名字都以 QDom 开头,因此,QDomElement就是 Element 节点,QDomText就是 Text 节点。不同类型的节点则有不同类型的子节点。例如,Element 节点允许包含其它 Element 节点,也可以是其它类型,比如 EntityReference,Text,CDATASection,ProcessingInstruction 和 Comment。按照 W3C 的规定,我们有如下的包含规则:

[Document]<- [Element]<- DocumentType<- ProcessingInstrument<- Comment
[Attr]<- [EntityReference]<- Text
[DocumentFragment] | [Element] | [EntityReference] | [Entity]<- [Element]<- [EntityReference]<- Text<- CDATASection<- ProcessingInstrument<- Comment

上面表格中,带有 [] 的可以带有子节点,反之则不能。

程序的目的还是一样的:用QTreeWidget 来显示这个文件的结构。需要注意的是,由于我们选用 DOM 方式处理 XML,无论是 Qt4 还是 Qt5 都需要在 .pro 文件中添加这么一句:
QT += xml

头文件也是类似的

class MainWindow : public QMainWindow
{Q_OBJECTpublic:explicit MainWindow(QWidget *parent = 0);~MainWindow();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;
private:Ui::MainWindow *ui;
};

构造函数与上面类似

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);
}

readFile文件发生率变化

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对象,代表整个文档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() != "bookindex"){QMessageBox::critical(this, tr("Error"),tr("Not a bookindex file"));return false;}parseBookindexElement(root);return true;}

readFile()函数显然更长更复杂。首先需要使用QFile打开一个文件,这点没有区别。然后我们创建一个QDomDocument对象,代表整个文档。注意看我们上面介绍的结构图,Document 是 DOM 树的根节点,也就是这里的QDomDocument;使用其setContent()函数填充 DOM 树。setContent()有八个重载,我们使用了其中一个:

bool QDomDocument::setContent ( QIODevice * dev,bool namespaceProcessing,QString * errorMsg = 0,int * errorLine = 0,int * errorColumn = 0 )

不过,这几个重载形式都调用同一实现

bool QDomDocument::setContent ( const QByteArray & data,bool namespaceProcessing,QString * errorMsg = 0,int * errorLine = 0,int * errorColumn = 0 )

两个函数的参数基本类似。第二个函数有五个参数,第一个是QByteArray,也就是所读取的真实数据,由QIODevice即可获得这个数据,而QFile就是QIODevice的子类;第二个参数确定是否处理命名空间,如果设置为 true,处理器会自动设置标签的前缀之类,因为我们的 XML 文档没有命名空间,所以直接设置为 false;剩下的三个参数都是关于错误处理。后三个参数都是输出参数,我们传入一个指针,函数会设置指针的实际值,以便我们在外面获取并进行进一步处理。

当QDomDocument::setContent()函数调用完毕并且没有错误后,我们调用QDomDocument::documentElement()函数获得一个 Document 元素。如果这个 Document 元素标签是 bookindex,则继续向下处理,否则则报错。

void MainWindow::parseBookindexElement(const QDomElement &element)
{QDomNode child = element.firstChild();//根标签下的子标签while (!child.isNull()){if (child.toElement().tagName() == "entry")//qdomnode ————》qdomelement的转换基类到子类的转换{parseEntryElement(child.toElement(),treeWidget->invisibleRootItem());}child = child.nextSibling();}}

如果根标签正确,我们取第一个子标签,判断子标签不为空,也就是存在子标签,然后再判断其名字是不是 entry。如果是,说明我们正在处理 entry 标签,则调用其自己的处理函数;否则则取下一个标签(也就是nextSibling()的返回值)继续判断。注意我们使用这个 if 只选择 entry 标签进行处理,其它标签直接忽略掉。另外,firstChild()和nextSibling()两个函数的返回值都是QDomNode。这是所有节点类的基类。当我们需要对节点进行操作时,我们必须将其转换成正确的子类。这个例子中我们使用toElement()函数将QDomNode转换成QDomElement。如果转换失败,返回值将是空的QDomElement类型,其tagName()返回空字符串,if 判断失败,其实也是符合我们的要求的。

void MainWindow::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()函数中,我们创建了一个树组件的节点,其父节点是根节点或另外一个 entry 节点。接着我们又开始遍历这个 entry 标签的子标签。如果是 entry 标签,则递归调用自身,并且把当前节点作为父节点;否则则调用parsePageElement()函数。

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);
}

parsePageElement()则比较简单,我们还是通过字符串拼接设置叶子节点的文本。

通过这个例子我们可以看到,使用 DOM 当时处理 XML 文档,除了一开始的setContent()函数,其余部分已经与原始文档没有关系了,也就是说,setContent()函数的调用之后,已经在内存中构建好了一个完整的 DOM 树,我们可以在这棵树上面进行移动,比如取相邻节点(nextSibling())。对比上一章流的方式,虽然我们早早关闭文件,但是我们始终使用的是readNext()向下移动,同时也不存在readPrevious()这样的函数。

用DOM方式读取xml相关推荐

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

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

  2. JavaXml教程(二)使用DOM方式解析XML文件

    DOM XML 解析方式是最容易理解的,它將XML文件作为Document对象读取到内存中,然后你可以轻松地遍历不同的元素和节点对象.遍历元素和节点不需要按照顺序进行. DOM解析方式适合尺寸较小的X ...

  3. JavaXml教程(四)使用DOM方式生成XML文件

    在前面的教程中,我们学习了使用DOM解析方式读取和修改XML文件内容,今天我们来学习如何使用DOM解析机制生成XML文件. 下面是我们对要生成的XML文件的具体要求: 1.根节点元素为"Em ...

  4. JavaXml教程(三)使用DOM方式修改XML文件内容

    DOM解析方式也可用于修改XML数据,我们可以使用它完成新增元素.删除元素.修改元素值.修改元素属性等操作. 我们的XML文件,内容如下: employee.xml <?xml version= ...

  5. 使用Python类似pandas的方式读取xml文件

    文章目录 0. 什么是XML文件? 1. 为什么使用pandas_read_xml读取XML 2. 安装和加载 3. 使用 3.1 例一 3.2 例二 3.3 例三 0. 什么是XML文件?   XM ...

  6. DOM方式解析XML文件实例

    books.XML文件: <?xml version="1.0" encoding="utf-8"?> <bookstore>      ...

  7. Java DOM方式解析XML(模板)

    1 //创建一个DocumentBuilderFactory工厂实例 2 DocumentBuilderFactory DBF=DocumentBuilderFactory.newInstance() ...

  8. DOM方式进行的XML文件、Document、String之间的相互转换

    http://kingxss.iteye.com/blog/1026954 XML文件test.xml: Xml代码 <?xml version="1.0" encoding ...

  9. 粤嵌学习打卡第19天(基于DOM方式解析、生成XML文档)

    今天我们来聊聊基于DOM方式实现解析和生成XML文档 一般web开发的xnl文档: 基于DOM处理XML文档 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SIQAET ...

最新文章

  1. hdu 2552 三足鼎立 关于tan的数论
  2. 如何很好的使用Linq的Distinct方法
  3. android适配右到左布局注意事项
  4. poj 2362(剪枝)
  5. 南瑞变压器保护装置说明书_电力试验:变压器感应耐压试验(二)
  6. boost::hana::at_c用法的测试程序
  7. Element UI 在父类设置样式不起作用
  8. 分享一个理工男必学的撩妹姿势
  9. Vue 2.0 入门系列(15)学习 Vue.js 需要掌握的 es6 (2)
  10. Linux 输入输出重定向 2>/dev/null和>/dev/null 2>1和2>1>/dev/nul
  11. 用java设计秒表_运用Java编写 秒表程序
  12. Web前端JavaScript笔记(3)对象
  13. 联通沃云服务器型号,云服务器
  14. Java回调函数实例
  15. RFC2544吞吐量测试详细步骤-MiniSMB-HurricaneII软件操作演示
  16. Eclipse+Java+Swing实现企业人事管理系统
  17. mysql基础命令(一)
  18. 【Flocking算法】海王的鱼塘是怎样炼成的
  19. Unity通过脚本实现不规则多边形的碰撞检测
  20. nvme命令中prp_Linux nvme驱动分析之块设备层

热门文章

  1. Linux C基础笔记(3)
  2. matlab plot 坐标轴标注,Matlab绘图坐标轴的设置教程
  3. iphonex屏幕出现一条绿线_关于对 iPhone 11、iPhone X? 采用的 Liquid 视网膜显示屏的误区,在此说明。...
  4. 计算机语言分类:机器语言、汇编语言、标记语言、脚本语言、编程语言
  5. bzoj 1014 火星人prefix —— splay+hash
  6. ExcelPackage 读取、导出excel
  7. 浅入浅出数据结构(18)——希尔排序
  8. shell脚本修改文本中匹配行之前的行的方法
  9. spring aop使用
  10. ftp 批量上传文件命令