DOC 文档对象模型
文档对象模型(Document Object Model,简称DOM),是W3C组织推荐的处理可扩展置标语言的标准编程接口。它是一种与平台和语言无关的应用程序接口(API),它可以动态地访问程序和脚本,更新其内容、结构和www文档的风格(HTML和XML文档是通过说明部分定义的)。文档可以进一步被处理,处理的结果可以加入到当前的页面。
DOM是一种基于树的API文档,它要求在处理过程中整个文档都表示在存储器中。另外一种简单的API是基于事件的SAX,它可以用于处理很大的XML文档,由于大,所以不适合全部放在存储器中处理。
先来了解一下xml基本要素:
QtXML基本结构
下面列出了Qt处理xml的一些类以及说明,加粗表示是常用。
操作XML
部署环境
通过Qt Create创建一个工程。然后在*.pro配置文件中添加xml model。
在添加Qt解析xml相关的头文件
#include <QtXml> #include <QDomDocument>
添加信息头
// xml文档 QDomDocument* m_Doc = new QDomDocument(); // 创建XML处理类,通常用于处理第一行描述信息 QDomProcessingInstruction instruction; std::string note = "version=1.0 encoding=utf-8"; // 创建XML头部格式 instruction = m_Doc->createProcessingInstruction(QString::fromStdString(type), QString::fromStdString(note)); // 添加到XML文件中 m_Doc->appendChild(instruction);
读取XML文件
// 打开文件 QFile m_File = new QFile("./1.xml"); if (false == m_File->open(mode)) { QMessageBox::information(NULL, u8"提示", u8"文件打开失败!"); return; } // 将doc与file关联起来 // 这个函数从IO设备dev中读取XML文档,如果内容被成功解析,则返回true;否则返回false。 if (false == m_Doc->setContent(m_File)) { QMessageBox::information(NULL, u8"提示", u8"操作的文件不是XML文件!"); m_File->close(); return ; }
添加根节点
通过createElement
方法创建的第一个子节点就是根节点。然后通过appendChild
方法将创建的节点添加到doc中。
// 创建根节点 QDomElement rootNode = m_Doc->createElement(rootName); // 添加到XML文件中 m_Doc->appendChild(rootNode);
添加一个没有属性的节点
添加一个没有属性的节点的方法和添加根节点的方法一致。
// 创建根节点 QDomElement rootNode = m_Doc->createElement(rootName); // 添加到XML文件中 m_Doc->appendChild(rootNode);
添加一个有属性的节点
std::list<std::pair<std::string, std::string>> Attribute{{"ID","2"},{"name","张三"}}; QDomElement node = m_Doc->createElement(nodeName); // 给节点创建属性 for(const auto& elem : Attribute) { // 参数一是字符串,参数二可以是任何类型 node.setAttribute(QString::fromStdString(elem.first), QString::fromStdString(elem.second)); } rootNode.appendChild(node);
添加一个元素节点
// 创建子元素 QDomElement node = m_Doc->createElement(nodeName); // 设置尖括号中的值 QDomText text = m_Doc->createTextNode(value); // 添加关系 node.appendChild(text); root.appendChild(node);
给节点单独设置属性
setAttribute
方法设置节点的属性名和属性值。
QDomElement elemNode = m_Doc->createElement(QString::fromStdString(name)); elemNode.setAttribute(QString::fromStdString(name), QString::fromStdString(value)); node.appendChild(elemNode);
删除所有同名节点
removeChild
方法使用需要注意:
看下面官方文档说明
Removes oldChild from the list of children. oldChild must be a direct child of this node. Returns a new reference to oldChild on success or a null node on failure.
类似于C++容器存在迭代器失效问题。所以我这里就从后向前删除了。
// 获取节点名字为Book的所有节点 QDomNodeList nodes = m_Doc->elementsByTagName(nodeName); for (int i = nodes.size() - 1; i >= 0; i--) { // 获取节点 QDomElement element = nodes.at(i).toElement(); // 所以 要么从后向前删除 要么就需要重新指定新节点的位置 root.removeChild(element); }
删除所有同名带属性的节点
QDomNodeList nodes = m_Doc->elementsByTagName(nodeName); for (int i = 0; i < nodes.count(); i++) { // 获取节点 QDomElement element = nodes.at(i).toElement(); bool isFind {true}; for(const auto& elem : attribute) { // 进行判断(返回属性的值进行判断) if (element.attribute(QString::fromStdString(elem.first)) != QString::fromStdString(elem.second)) { isFind = false; } } if(true == isFind) { root.removeChild(nodes.at(i)); } }
递归删除所有同名的节点
// 获取节点名字为Book的所有节点 QDomNodeList nodes = m_Doc->elementsByTagName(QString::fromStdString(nodename)); // QDomElement root = GetRootNode(); for (int i = nodes.size() - 1; i >= 0; i--) { // 获取节点 QDomElement element = nodes.at(i).toElement(); // 获取父节点 QDomNode fNode = element.parentNode(); fNode.removeChild(element); }
保存XML
QFile* m_File= new QFile(fileName); if (false == m_File->open(QFileDevice::WriteOnly | QFileDevice::Truncate)) { QMessageBox::information(NULL, "提示", "文件打开失败!"); return; } QTextStream stream(m_File); m_Doc->save(stream, retract);
获取节点元素
if (node.toElement().tagName() == name) { return node; }
获取节点元素的值
if (node.toElement().tagName() == name) { return node.toElement().text().toStdString(); }
获取该节点的属性值
key为属性名
if (false == node.isNull() && true == node.toElement().hasAttribute(key)) { value = node.toElement().attributeNode(key).value(); }
完整代码
注:必须支持C++17
OperateXML.h
#ifndef OPERATEXML_H #define OPERATEXML_H // 读取xml必须要包含的 #include <QtXml> #include <QDomDocument> #include <QIODevice> #include <QMessageBox> //#include <QtCore5Compat/QTextCodec> #include <string> #include <map> class OperateXML { public: explicit OperateXML(/*std::string filename = std::string("")*/); ~OperateXML(); // 创建一个xml文件 bool CreateXmlFile(std::string filename = std::string(""), QIODevice::OpenMode mode = QFileDevice::WriteOnly | QFileDevice::Truncate); // 添加一个信息头 void CreateXmlHeader(std::string type = std::string("xml"), std::string version = "1.0", std::string encoding = std::string("UTF-8")); // 加载已有的xml文件 bool LoadXml(std::string filename, QIODevice::OpenMode mode = QFileDevice::ReadOnly); // 获取根节点 Stu QDomElement GetRootNode(); // 创建一个根节点 Stu QDomElement CreateXmlRootNode(std::string rootName = std::string("root")); // 添加一个没有属性的节点 <StuInfo/> Stu StuInfo QDomElement AddNoAttributesNode(QDomElement fNode, std::string nodeName); // 添加一个有属性的节点 <StuInfo ID = "1"> Stu StuInfo ID 1 Name 张三 QDomElement AddAttributesNode(QDomElement fNode, std::string nodeName, std::list<std::pair<std::string, std::string>> Attribute); // 添加元素节点 QDomElement AddElementNode(QDomElement fNode, std::string nodeName, std::string value); // 设置节点属性 QDomElement SetNodeAttribute(QDomElement node, std::string name, std::string value); // 删除节点 删除当前节点下的所有名为nodename的节点 void DeleteNodes(QDomElement fNode, std::string nodename); // 删除节点 删除当前节点下的所有名为nodename的节点 且属性值相同 void DeleteNode(QDomElement fNode, std::string nodename, std::list<std::pair<std::string, std::string>> attribute); // 删除节点 删除所有节点下名为nodename的节点 void DeleteNodes(std::string nodename); // 删除节点 删除所有节点下名为nodename的节点 且属性值相同 void DeleteNode(std::string nodename, std::list<std::pair<std::string, std::string>> attribute); // 保存Xml 进位空格数 void SaveXml(int retract = 4); // 获取当前节点以及同级下的兄弟节点 且名为name的节点 如果为第一个孩子节点记得传入第三个参数为true QDomElement GetElem(QDomElement& node, std::string name, bool first = false); QDomElement GetElem(QDomElement& node, QString name, bool first = false); // 获取当前节点以及同级下的兄弟节点 且名为name的节点的值 std::string GetElemValue(QDomElement& node, std::string name, bool first = false); QString GetElemValue(QDomElement& node, QString name, bool first = false); // 获取 该节点指定的 属性节点值 std::string GetElemAttributeValue(QDomElement node, std::string key); QString GetElemAttributeValue(QDomElement node, QString key); private: QDomDocument* m_Doc; QFile* m_File; std::string m_FileName; }; #endif // OPERATEXML_H
OperateXML.cpp
#include "OperateXML.h" OperateXML::OperateXML(/*std::string filename*/) : m_Doc(new QDomDocument()) , m_File{nullptr} { } OperateXML::~OperateXML() { if(nullptr != m_Doc) { delete m_Doc; m_Doc = nullptr; } if(nullptr != m_File) { m_File->close(); delete m_File; m_File = nullptr; } } bool OperateXML::CreateXmlFile(std::string filename, QIODevice::OpenMode mode) { if(true == filename.empty()) { QMessageBox::information(NULL, u8"提示", u8"文件名为空!"); return false; } if(nullptr != m_File) { delete m_File; m_File = nullptr; } m_File = new QFile(QString::fromStdString(filename)); if(false == m_File->open(mode)) { QMessageBox::information(NULL, u8"提示", u8"文件打开或创建失败!"); return false; } if(false == m_File->isOpen()) { QMessageBox::information(NULL, u8"提示", u8"文件打开或创建失败!"); return false; } return true; } void OperateXML::CreateXmlHeader(std::string type, std::string version, std::string encoding) { // 创建XML处理类,通常用于处理第一行描述信息 QDomProcessingInstruction instruction; std::string note = std::string("version=\"") + version + std::string("\" ") + std::string("encoding=\"") + encoding + std::string("\""); // 创建XML头部格式 instruction = m_Doc->createProcessingInstruction(QString::fromStdString(type), QString::fromStdString(note)); // 添加到XML文件中 m_Doc->appendChild(instruction); } bool OperateXML::LoadXml(std::string filename, QIODevice::OpenMode mode) { // 打开文件 m_File = new QFile(QString::fromStdString(filename)); if (false == m_File->open(mode)) { QMessageBox::information(NULL, u8"提示", u8"文件打开失败!"); return false; } // 将doc与file关联起来 // 这个函数从IO设备dev中读取XML文档,如果内容被成功解析,则返回true;否则返回false。 if (false == m_Doc->setContent(m_File)) { QMessageBox::information(NULL, u8"提示", u8"操作的文件不是XML文件!"); m_File->close(); return false; } return true; } QDomElement OperateXML::GetRootNode() { QDomElement root = m_Doc->documentElement(); return root; } QDomElement OperateXML::CreateXmlRootNode(std::string rootName) { // 创建根节点 QDomElement rootNode = m_Doc->createElement(QString::fromStdString(rootName)); // 添加到XML文件中 m_Doc->appendChild(rootNode); return rootNode; } QDomElement OperateXML::AddNoAttributesNode(QDomElement fNode, std::string nodeName) { if(true == fNode.isNull()) { return QDomElement(); } QDomElement node = m_Doc->createElement(QString::fromStdString(nodeName)); fNode.appendChild(node); return node; } QDomElement OperateXML::AddAttributesNode(QDomElement fNode, std::string nodeName, std::list<std::pair<std::string, std::string>> Attribute) { QDomElement node = m_Doc->createElement(QString::fromStdString(nodeName)); // 给节点创建属性 for(const auto& elem : Attribute) { // 参数一是字符串,参数二可以是任何类型 node.setAttribute(QString::fromStdString(elem.first), QString::fromStdString(elem.second)); } fNode.appendChild(node); return node; } QDomElement OperateXML::AddElementNode(QDomElement fNode, std::string nodeName, std::string value) { // 创建子元素 QDomElement node = m_Doc->createElement(QString::fromStdString(nodeName)); // 设置尖括号中的值 QDomText text = m_Doc->createTextNode(QString::fromStdString(value)); // 添加关系 node.appendChild(text); fNode.appendChild(node); return node; } QDomElement OperateXML::SetNodeAttribute(QDomElement node, std::string name, std::string value) { QDomElement elemNode = m_Doc->createElement(QString::fromStdString(name)); elemNode.setAttribute(QString::fromStdString(name), QString::fromStdString(value)); node.appendChild(elemNode); return node; } void OperateXML::DeleteNodes(QDomElement fNode, std::string nodename) { // 获取节点名字为Book的所有节点 QDomNodeList nodes = m_Doc->elementsByTagName(QString::fromStdString(nodename)); for (int i = nodes.size() - 1; i >= 0; i--) { // 获取节点 QDomElement element = nodes.at(i).toElement(); /* * Removes oldChild from the list of children. oldChild must be a direct child of this node. Returns a new reference to oldChild on success or a null node on failure. 所以 要么从后向前删除 要么就需要重新指定新节点的位置 */ fNode.removeChild(element); } } void OperateXML::DeleteNode(QDomElement fNode, std::string nodename, std::list<std::pair<std::string, std::string>> attribute) { QDomNodeList nodes = m_Doc->elementsByTagName(QString::fromStdString(nodename)); for (int i = 0; i < nodes.count(); i++) { // 获取节点 QDomElement element = nodes.at(i).toElement(); bool isFind {true}; for(const auto& elem : attribute) { // 进行判断(返回属性的值进行判断) if (element.attribute(QString::fromStdString(elem.first)) != QString::fromStdString(elem.second)) { isFind = false; } } if(true == isFind) { fNode.removeChild(nodes.at(i)); } } } void OperateXML::DeleteNodes(std::string nodename) { // 获取节点名字为Book的所有节点 QDomNodeList nodes = m_Doc->elementsByTagName(QString::fromStdString(nodename)); // QDomElement root = GetRootNode(); for (int i = nodes.size() - 1; i >= 0; i--) { // 获取节点 QDomElement element = nodes.at(i).toElement(); /* * Removes oldChild from the list of children. oldChild must be a direct child of this node. Returns a new reference to oldChild on success or a null node on failure. 所以 要么从后向前删除 要么就需要重新指定新节点的位置 */ QDomNode fNode = element.parentNode(); fNode.removeChild(element); } } void OperateXML::DeleteNode(std::string nodename, std::list<std::pair<std::string, std::string> > attribute) { QDomNodeList nodes = m_Doc->elementsByTagName(QString::fromStdString(nodename)); for (int i = 0; i < nodes.count(); i++) { // 获取节点 QDomElement element = nodes.at(i).toElement(); bool isFind {true}; for(const auto& elem : attribute) { // 进行判断(返回属性的值进行判断) if (element.attribute(QString::fromStdString(elem.first)) != QString::fromStdString(elem.second)) { isFind = false; } } if(true == isFind) { QDomNode fNode = element.parentNode(); fNode.removeChild(nodes.at(i)); } } } void OperateXML::SaveXml(int retract) { m_File->close(); if (false == m_File->open(QFileDevice::WriteOnly | QFileDevice::Truncate)) { QMessageBox::information(NULL, "提示", "文件打开失败!"); return; } QTextStream stream(m_File); m_Doc->save(stream, retract); } QDomElement OperateXML::GetElem(QDomElement& node, std::string name, bool first) { if(true == first) { node = node.firstChild().toElement(); } while(false == node.isNull()) { if (node.toElement().tagName() == QString::fromStdString(name)) { return node; } node = node.nextSibling().toElement(); } return QDomElement(); } QDomElement OperateXML::GetElem(QDomElement &node, QString name, bool first) { if(true == first) { node = node.firstChild().toElement(); } while(false == node.isNull()) { // qDebug() << node.toElement().tagName(); if (node.toElement().tagName() == name) { return node; } node = node.nextSibling().toElement(); } return QDomElement(); } std::string OperateXML::GetElemValue(QDomElement& node, std::string name, bool first) { if(true == first) { node = node.firstChild().toElement(); } while(false == node.isNull()) { // qDebug() << node.toElement().tagName(); if (node.toElement().tagName() == QString::fromStdString(name)) { return node.toElement().text().toStdString(); } node = node.nextSibling().toElement(); } return std::string(); } QString OperateXML::GetElemValue(QDomElement &node, QString name, bool first) { if(true == first) { node = node.firstChild().toElement(); } while(false == node.isNull()) { // qDebug() << node.toElement().tagName(); if (node.toElement().tagName() == name) { return node.toElement().text(); } node = node.nextSibling().toElement(); } return QString(); } std::string OperateXML::GetElemAttributeValue(QDomElement node, std::string key) { std::string value; if (false == node.isNull() && true == node.toElement().hasAttribute(QString::fromStdString(key))) { value = node.toElement().attributeNode(QString::fromStdString(key)).value().toStdString(); } return value; } QString OperateXML::GetElemAttributeValue(QDomElement node, QString key) { QString value; if (false == node.isNull() && true == node.toElement().hasAttribute(key)) { value = node.toElement().attributeNode(key).value(); } return value; }