1. 总述
XML是一种类似于HTML的标记语言,但它的设计目的是用来传输数据,而不是显示数据。
XML的标签没有被预定义,用户需要在使用时自行进行定义。
相对于数据库表格的二维表示,XML使用的树形结构更能表现出数据的包含关系。
作为一种文本文件格式,XML简单明了的特性使得它在信息存储和描述领域非常流行。
在Qt中提供了QtXml模块来进行XML文档的处理。
我们在Qt帮助中输入关键字QtXml Module,可以看到该模块的类表。
这里主要提供了三种解析方法:
DOM方法: 可以进行读写;
SAX方法: 可以进行读取;
基于流的方法:分别使用QXmlStreamReader和QXmlStreamWriter进行读取和写入。
注1:要在项目中使用QtXml模块,还需要在项目文件(.pro文件)中添加 "QT += xml" 一行代码。
注2: 在 Qt4 中,这三种方式都位于 QtXml 模块中。Qt5 则将 QXmlStreamReader/QXmlStreamWriter 移动到 QtCore 中
2. 标准的xml示例
<?xml version="1.0" encoding="UTF-8"?> //xml说明
<library>
<book id="01">
<title>Qt</title>
<author>shiming</author>
</book>
<book id="02">
<title>Linux</title>
<author>yafei</author>
</book>
</library>
1> xml说明:对XML文档处理的环境和要求的说明,encoding=“UTF-8”是使用的编码,
指出文档是使用何种字符集建立的,默认值为Unicode 编码
2> xml元素:一个元素由起始标签<标签名>和终止标签</标签名>以及两个标签之间的内容组成,
而文档中第一个元素被称为根元素,比如这里的<library></library>,
XML文档必须有且只有一个根元素
3> 元素可以包含属性,用来描述元素的相关信息,属性名和属性值在元素的起始标签中给出,
格式为<元素名 属性名=“属性值”>,如<book id=“01”>,属性值必须在单引号或者双引号中。
4> 在元素中可以包含子元素,也可以只包含文本内容,比如这里的<title>Qt</title>中的Qt就是文本内容。
3. DOM方式
1> 在QDom中,是将整个XML文件读到内存中的doc对象中的。然后使用节点(QDomNode )操作doc对象,
像XML说明,元素,属性,文本等等都被看做是节点,这样就使得操作XML文档变得很简单,
我们只需通过转换函数将节点转换成相应的类型,如:QDomElement e =n.toElement();
2> 优点: 将整个 XML 文档读入内存,构建成一个树结构,允许程序在树结构上向前向后移动导航,
这是与另外两种方式最大的区别,也就是允许实现多次解析器.
3> 缺点:DOM需要预先把整个XML文档都读入内存,这样就使得它不适合处理较大的文件。
4> 创建节点,将其写入XML文件,主要操作包括:
1). 打开文件
QFile file(filepath);//通过文件路径打开
file.open(QIODevice::ReadWrite);//打开文件方式
2). 添加xml第一行(xml文档说明)
QDomDocument doc;
QDomProcessingInstruction instruction;
instruction = doc.createProcessingInstruction("xml","version=\"1.0\" encoding=\"UTF-8\"");
doc.appendChild(instruction);
3). 创建根节点
QDomElement root = doc.createElement("library");//添加根元素
4). 创建其他节点
QDomElement booknode = doc.createElement("book");
QDomElement titlenode = doc.createElement("title");
QDomElement authornode = doc.createElement("author");
5). 创建节点属性
//方式一:创建属性 其中键值对的值可以是各种类型
booknode.setAttribute("id",01);
//方式二:创建属性 值必须是字符串
QDomAttr idattr=doc.createAttribute("id"); //创建key值
idattr.setValue("2013/6/13"); //创建对应value值
booknode.setAttributeNode(idattr); //绑定对应到节点上
6). 创建节点文本内容
QDomText text;
text = doc.createTextNode("Qt");
titlenode.appendChild(text);
text.clear();
text = doc.createTextNode("shiming");
authornode.appendChild(text);
text.clear();
7). 将节点按照逻辑串起来
booknode.appendChild(authornode);
booknode.appendChild(titlenode);
root.appendChild(booknode);
doc.appendChild(root);//注意这一步,容易忽略
8). 写入到文件中,保存关闭文件
QTextStream out(&file);
doc.save(out,4);//设置缩进为4个空格
file.close();
创建一个便签节点后,若要继续添加便签节点,则需要在已有节点后增加一个新节点,并重写入XML文件。
5> 操作节点信息,主要操作包括:
QDomDocument doc;
1).读取根节点:
QDomElement root = doc.documentElement();
2).读取根节点的第一个子节点:
QDomNode node = root.firstChild();
3).读取节点的下一个同级节点:
node = node.nextSibling();
4).读取根节点的最后一个子节点:
QDomNode lastnode = root.lastChild();
5).匹配节点标记:
node.toElement().tagName() == "book";
6).匹配节点key为"id"的value值:
node.toElement().attribute("id") == "01";
7).匹配节点的文本内容:
6> 删除便签时,要删除相应的XML节点.
用到的主要函数为:root.removeChild(node);
但在删除某个节点后要重写整个文件,因此在文件的打开方式中要加上QIODevice::Truncate,表示覆盖重写。
QFile file(filepath);
file.open(QIODevice::ReadOnly);
file.close();
//按照节点的key-value值比较来删除
QDomNodeList list = doc.elementsByTagName("book");//取出所有的book元素,放入list容器内
for(int i=0; i<list.count(); i++)//循环比较
{
QDomElement e = list.at(i).toElement();
if(e.attribute(tr("id")) == "01")//取book节点的value值与字符串"01"比较,如果相同
{
QDomElement root = doc.documentElement(); //取出根节点
root.removeChild(list.at(i)); //从根节点上删除该节点
qDebug() << "remove ok !";
if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate))//删除时要覆盖重写
return ;
QTextStream out(&file);
doc.save(out,4);
file.close();
}
}
//按照节点的文本内容来删除
QDomNodeList list = doc.elementsByTagName("book");//取出所有的book元素,放入list容器内
for(int i=0; i<list.count(); i++)//循环比较
{
QDomElement booknode = list.at(i).toElement();
QDomElement titlenode = booknode.firstChildElement();
if(titlenode.toElement().text() == "Qt") //当节点文本内容一致时
{
QDomElement root = doc.documentElement(); //取出根节点
root.removeChild(list.at(i)); //从根节点上删除该book节点
qDebug() << "remove ok !";
if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate))//删除时要覆盖重写
return ;
QTextStream out(&file);
doc.save(out,4);
file.close();
}
}
4. SAX方式
1> 优点:它提供了比DOM更简单的接口,并且它不需要将整个XML文档一次性读入内存,这样便可以用来读取较大的文件。
2> 缺点:只能读取并显示整个XML文档
3> 流程:在Qt的QtXml模块中提供了一个QXmlSimpleReader的类,它便是基于SAX的XML解析器。
这个解析器是基于事件的,但这些事件由它们自身进行关联,我们并不需要进行设置。
我们只需知道,当解析器解析一个XML的元素时,就会执行相应的事件,
我们只要重写这些事件处理函数,就能让它按照我们的想法进行解析。
比如要解析下面的元素:
<title>Qt</title>
解析器会依次调用如下事件处理函数:startElement(),characters(),endElement()。
我们可以在startElement()中获得元素名(如“title”)和属性,
在characters()中获得元素中的文本(如“Qt”),
在endElement()中进行一些结束读取该元素时想要进行的操作。
而所有的这些事件处理函数我们都可以通过继承QXmlDefaultHandler类来重写。
5. 流方法
一种快速的基于流的方式访问良格式 XML 文档,特别适合于实现一次解析器(所谓“一次解析器”,可以理解
成我们只需读取文档一次,然后像一个遍历器从头到尾一次性处理 XML 文档,期间不会有反复的情况,
也就是不会读完第一个标签,然后读第二个,读完第二个又返回去读第一个,这是不允许的);
XML是一种类似于HTML的标记语言,但它的设计目的是用来传输数据,而不是显示数据。
XML的标签没有被预定义,用户需要在使用时自行进行定义。
相对于数据库表格的二维表示,XML使用的树形结构更能表现出数据的包含关系。
作为一种文本文件格式,XML简单明了的特性使得它在信息存储和描述领域非常流行。
在Qt中提供了QtXml模块来进行XML文档的处理。
我们在Qt帮助中输入关键字QtXml Module,可以看到该模块的类表。
这里主要提供了三种解析方法:
DOM方法: 可以进行读写;
SAX方法: 可以进行读取;
基于流的方法:分别使用QXmlStreamReader和QXmlStreamWriter进行读取和写入。
注1:要在项目中使用QtXml模块,还需要在项目文件(.pro文件)中添加 "QT += xml" 一行代码。
注2: 在 Qt4 中,这三种方式都位于 QtXml 模块中。Qt5 则将 QXmlStreamReader/QXmlStreamWriter 移动到 QtCore 中
2. 标准的xml示例
<?xml version="1.0" encoding="UTF-8"?> //xml说明
<library>
<book id="01">
<title>Qt</title>
<author>shiming</author>
</book>
<book id="02">
<title>Linux</title>
<author>yafei</author>
</book>
</library>
1> xml说明:对XML文档处理的环境和要求的说明,encoding=“UTF-8”是使用的编码,
指出文档是使用何种字符集建立的,默认值为Unicode 编码
2> xml元素:一个元素由起始标签<标签名>和终止标签</标签名>以及两个标签之间的内容组成,
而文档中第一个元素被称为根元素,比如这里的<library></library>,
XML文档必须有且只有一个根元素
3> 元素可以包含属性,用来描述元素的相关信息,属性名和属性值在元素的起始标签中给出,
格式为<元素名 属性名=“属性值”>,如<book id=“01”>,属性值必须在单引号或者双引号中。
4> 在元素中可以包含子元素,也可以只包含文本内容,比如这里的<title>Qt</title>中的Qt就是文本内容。
3. DOM方式
1> 在QDom中,是将整个XML文件读到内存中的doc对象中的。然后使用节点(QDomNode )操作doc对象,
像XML说明,元素,属性,文本等等都被看做是节点,这样就使得操作XML文档变得很简单,
我们只需通过转换函数将节点转换成相应的类型,如:QDomElement e =n.toElement();
2> 优点: 将整个 XML 文档读入内存,构建成一个树结构,允许程序在树结构上向前向后移动导航,
这是与另外两种方式最大的区别,也就是允许实现多次解析器.
3> 缺点:DOM需要预先把整个XML文档都读入内存,这样就使得它不适合处理较大的文件。
4> 创建节点,将其写入XML文件,主要操作包括:
1). 打开文件
QFile file(filepath);//通过文件路径打开
file.open(QIODevice::ReadWrite);//打开文件方式
2). 添加xml第一行(xml文档说明)
QDomDocument doc;
QDomProcessingInstruction instruction;
instruction = doc.createProcessingInstruction("xml","version=\"1.0\" encoding=\"UTF-8\"");
doc.appendChild(instruction);
3). 创建根节点
QDomElement root = doc.createElement("library");//添加根元素
4). 创建其他节点
QDomElement booknode = doc.createElement("book");
QDomElement titlenode = doc.createElement("title");
QDomElement authornode = doc.createElement("author");
5). 创建节点属性
//方式一:创建属性 其中键值对的值可以是各种类型
booknode.setAttribute("id",01);
//方式二:创建属性 值必须是字符串
QDomAttr idattr=doc.createAttribute("id"); //创建key值
idattr.setValue("2013/6/13"); //创建对应value值
booknode.setAttributeNode(idattr); //绑定对应到节点上
6). 创建节点文本内容
QDomText text;
text = doc.createTextNode("Qt");
titlenode.appendChild(text);
text.clear();
text = doc.createTextNode("shiming");
authornode.appendChild(text);
text.clear();
7). 将节点按照逻辑串起来
booknode.appendChild(authornode);
booknode.appendChild(titlenode);
root.appendChild(booknode);
doc.appendChild(root);//注意这一步,容易忽略
8). 写入到文件中,保存关闭文件
QTextStream out(&file);
doc.save(out,4);//设置缩进为4个空格
file.close();
创建一个便签节点后,若要继续添加便签节点,则需要在已有节点后增加一个新节点,并重写入XML文件。
5> 操作节点信息,主要操作包括:
QDomDocument doc;
1).读取根节点:
QDomElement root = doc.documentElement();
2).读取根节点的第一个子节点:
QDomNode node = root.firstChild();
3).读取节点的下一个同级节点:
node = node.nextSibling();
4).读取根节点的最后一个子节点:
QDomNode lastnode = root.lastChild();
5).匹配节点标记:
node.toElement().tagName() == "book";
6).匹配节点key为"id"的value值:
node.toElement().attribute("id") == "01";
7).匹配节点的文本内容:
titlenode.toElement().text() == "Qt";
8).修改author节点的text内容
QDomNode oldnode = authornode.firstChild();
authornode.firstChild().setNodeValue("new authorname"); //赋予新的内容
QDomNode newnode = authornode.firstChild(); //值修改过后
authornode.replaceChild(newnode,oldnode); //调用节点的
6> 删除便签时,要删除相应的XML节点.
用到的主要函数为:root.removeChild(node);
但在删除某个节点后要重写整个文件,因此在文件的打开方式中要加上QIODevice::Truncate,表示覆盖重写。
QFile file(filepath);
file.open(QIODevice::ReadOnly);
file.close();
//按照节点的key-value值比较来删除
QDomNodeList list = doc.elementsByTagName("book");//取出所有的book元素,放入list容器内
for(int i=0; i<list.count(); i++)//循环比较
{
QDomElement e = list.at(i).toElement();
if(e.attribute(tr("id")) == "01")//取book节点的value值与字符串"01"比较,如果相同
{
QDomElement root = doc.documentElement(); //取出根节点
root.removeChild(list.at(i)); //从根节点上删除该节点
qDebug() << "remove ok !";
if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate))//删除时要覆盖重写
return ;
QTextStream out(&file);
doc.save(out,4);
file.close();
}
}
//按照节点的文本内容来删除
QDomNodeList list = doc.elementsByTagName("book");//取出所有的book元素,放入list容器内
for(int i=0; i<list.count(); i++)//循环比较
{
QDomElement booknode = list.at(i).toElement();
QDomElement titlenode = booknode.firstChildElement();
if(titlenode.toElement().text() == "Qt") //当节点文本内容一致时
{
QDomElement root = doc.documentElement(); //取出根节点
root.removeChild(list.at(i)); //从根节点上删除该book节点
qDebug() << "remove ok !";
if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate))//删除时要覆盖重写
return ;
QTextStream out(&file);
doc.save(out,4);
file.close();
}
}
4. SAX方式
1> 优点:它提供了比DOM更简单的接口,并且它不需要将整个XML文档一次性读入内存,这样便可以用来读取较大的文件。
2> 缺点:只能读取并显示整个XML文档
3> 流程:在Qt的QtXml模块中提供了一个QXmlSimpleReader的类,它便是基于SAX的XML解析器。
这个解析器是基于事件的,但这些事件由它们自身进行关联,我们并不需要进行设置。
我们只需知道,当解析器解析一个XML的元素时,就会执行相应的事件,
我们只要重写这些事件处理函数,就能让它按照我们的想法进行解析。
比如要解析下面的元素:
<title>Qt</title>
解析器会依次调用如下事件处理函数:startElement(),characters(),endElement()。
我们可以在startElement()中获得元素名(如“title”)和属性,
在characters()中获得元素中的文本(如“Qt”),
在endElement()中进行一些结束读取该元素时想要进行的操作。
而所有的这些事件处理函数我们都可以通过继承QXmlDefaultHandler类来重写。
5. 流方法
一种快速的基于流的方式访问良格式 XML 文档,特别适合于实现一次解析器(所谓“一次解析器”,可以理解
成我们只需读取文档一次,然后像一个遍历器从头到尾一次性处理 XML 文档,期间不会有反复的情况,
也就是不会读完第一个标签,然后读第二个,读完第二个又返回去读第一个,这是不允许的);