创建项目
- 创建一个QtDomXmlTest的widget项目。Qt如何创建项目,网上有很多教程,这里就不介绍了。
- 修改.pro文件,增加xml支持
读写操作
为了方便管理,这里自己单独创建了一个操作xml的基类,所有的读写接口都在基类里实现,以后也可以扩展。
注意:这里需要包含头文件QtXml
创建xml文件
- QDomDocument:doc文档
- QDomProcessingInstruction:xml文档说明
- QDomElement:xml元素
/** * @funtion createDomXml * @brief create xml * @param strRoot Root 节点 * @return no */ bool DomXmlBase::createDomXml(QString strRoot) { QDomDocument doc; // 添加处理指令即XML说明 QDomProcessingInstruction instruction; instruction = doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\""); doc.appendChild(instruction); // 添加根元素 QDomElement root = doc.createElement(strRoot); doc.appendChild(root); QFile file(m_strFileName); if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { return false; } QTextStream out(&file); // 将文档保存到文件,4为子元素缩进字符数 doc.save(out, 4); file.close(); return true; }
通过doc对象创建一个xml文件同时包含一个根节点stroot。注:这里的m_strFileName是该类的私有变量,通过构造时赋值的
插入一个元素
/** * @funcname addDomXml * @brief 插入一个节点 * @param strId 序号 * @param strElementListText <元素值> * @return void * @example * <Peoples> * <People Id="001"> * <Name></Name> * <Age></Age> * <Gender></Gender> * <Hobby></Hobby> * .... * </People> * </Peoples> */ void DomXmlBase::addDomXml(QString strId, QList<QString> strElementListText) { if(strElementListText.isEmpty()) { qDebug() << "strElementListText is empty"; return; } /*1. open file*/ QFile file(m_strFileName); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) return; QDomDocument doc; if (!doc.setContent(&file)) { qDebug() << "doc.setContent failed"; file.close(); return; } file.close(); /*2. add element*/ QDomElement root = doc.documentElement(); if(m_RootElement.isEmpty()) return; /*root节点下第一元素*/ QDomElement device = doc.createElement(m_RootElement); /*为节点赋属性*/ QDomAttr id = doc.createAttribute("Id"); QList<QDomElement> domElementList; id.setValue(strId); device.setAttributeNode(id); int count = strElementListText.count(); int Elementcount = m_ElementList.count(); if(count != Elementcount) { qDebug() << "text count != Elementcount"; QMessageBox::critical(NULL,"注意","text count != Elementcount"); return ; } for (int i = 0; i < count; ++i) { QString strElementkey = m_ElementList.at(i); QString strElementvalue = strElementListText.at(i); QDomElement domElement = doc.createElement(strElementkey); QDomText text; text = doc.createTextNode(strElementvalue); domElement.appendChild(text); device.appendChild(domElement); } root.appendChild(device); /*3.save xml*/ if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { return ; } QTextStream out(&file); doc.save(out, 4); file.close(); qDebug() << "add Element xml"; }
- m_RootElement:根节点下第一元素
- m ElementList:m RootElement的所有子元素
- 这两个值是在父类构造函数调用setXmlElement初始化的,设计这两个成员变量的初衷是为了让这个类适应同类型结构的所有xml,可以任意修改元素标签以及增加元素的个数。
/** * @funcname setXmlElement * @brief 设置根元素标签,元素名称,返回根元素下个数。需先调用 * @param strRootElement * @param strList * @return count */ int DomXmlBase::setXmlElement(QString strRootElement, QList<QString> strList) { m_ElementList = strList; m_RootElement = strRootElement; return strList.count(); }
具体用法如下:
void Widget::on_btnAdd_clicked() { DomXmlBase xml("info.xml"); QList<QString> strList; strList << "Name" << "Age" << "Gender" << "Hobby"; xml.setXmlElement("people",strList); QList<QString> strListText; strListText << "Tom" << "22" << tr("男") << tr("跑步"); xml.addDomXml("001",strListText); }
或者
删除一条记录
这里是按进行ID查找的,如果检测到id相同则,删除。
/** * @funcname deleteDomXml * @brief delete a Element * @param strId * @return no */ void DomXmlBase::deleteDomXml(QString strId) { QFile file(m_strFileName); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) return ; QDomDocument doc; if (!doc.setContent(&file)) { file.close(); return ; } file.close(); // 以标签名进行查找 if(m_RootElement.isEmpty()) return; QDomNodeList list = doc.elementsByTagName(m_RootElement); for(int i=0; i<list.count(); i++) { QDomElement e = list.at(i).toElement(); if(e.attribute("Id") == strId) //如果相等测更新或者删除元素值 { // 如果元素的“编号”属性值与我们所查的相同 // 如果是删除操作 QDomElement root = doc.documentElement(); // 从根节点上删除该节点 root.removeChild(list.at(i)); QFile file(m_strFileName); if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) return ; QTextStream out(&file); doc.save(out,4); file.close(); qDebug() << "delete Element xml"; } } }
修改一条记录
这里是修改全部子元素的记录,基本上和添加相似,主要熟悉一下接口的使用。
/** * @funcname updateDomXml * @brief update Element * @param strId * @param strListText * @return no */ void DomXmlBase::updateDomXml(QString strId, QList<QString> strListText) { if(strListText.count()<m_ElementList.count()) { QMessageBox::critical(NULL,"注意","strListText个数小于m_ElementList"); return ; } QFile file(m_strFileName); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) return ; QDomDocument doc; if (!doc.setContent(&file)) { file.close(); return ; } file.close(); // 以标签名进行查找 if(m_RootElement.isEmpty()) return; QDomNodeList list = doc.elementsByTagName(m_RootElement); for(int i=0; i<list.count(); i++) { QDomElement e = list.at(i).toElement(); if(e.attribute("Id") == strId) { // 如果元素的“编号”属性值与我们所查的相同 QDomNodeList child = list.at(i).childNodes(); for(int j = 0; j < child.count(); j++) { QString strText = child.at(j).toElement().text(); QString strSetValue = strListText.at(j); if(strText != strSetValue) { child.at(j).toElement().firstChild().setNodeValue(strSetValue); } } QFile file(m_strFileName); if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) return ; QTextStream out(&file); doc.save(out,4); file.close(); qDebug() << "update Element xml"; break; } } }
读一条记录
/** * @funcname readDomXml * @brief 根据id获取其下元素值 * @param strId * @param strListText output param * @return no */ void DomXmlBase::readDomXml(QString strId, QList<QString> &strListText) { QFile file(m_strFileName); if(!strListText.isEmpty()) strListText.clear(); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) return ; QDomDocument doc; if (!doc.setContent(&file)) { file.close(); return ; } file.close(); // 以标签名进行查找 if(m_RootElement.isEmpty()){ QMessageBox::critical(NULL,"注意","请先设置setXmlElement"); return; } QDomNodeList list = doc.elementsByTagName(m_RootElement); for(int i=0; i<list.count(); i++) { QDomElement e = list.at(i).toElement(); if(e.attribute("Id") == strId) { // 如果元素的“编号”属性值与我们所查的相同 QDomNodeList child = list.at(i).childNodes(); for(int j = 0;j < child.count(); j++) { QString strText; strText = child.at(j).toElement().text(); strListText.append(strText); } break; } } }
可以看到readDomXml,updateDomXml,deleteDomXml操作基本相似,几个接口输入输出差异也不大,可以继续封装,根据操作进行操作,最小范围内暴露出一些接口。
doXml(QString operate,QString strId,QList<QString> strListText);
总结
本文主要介绍了QT通过dom操作xml,主要是一些二进制代码实现,希望读者能够通过此文,熟悉xml操作,并且可以自行封装自己想要的接口。
不足之处:如果不在父类构造函数初始化setXmlElement,每次创建基类对象后,都需要手动setXmlElement,否则查询时,找不到元素标签。