<?xml version="1.0" encoding="UTF-8"?>
<Soft>
<Name>Soft1</Name>
<Author>xx</Author>
<Version>v1.0</Version>
<Explain>test</Explain>
<Scans>
<Scan>
<Name>untitled2.ui</Name>
<SavePath>/home/MainProject/untitled2.ui</SavePath>
</Scan>
</Scans>
</Soft>
以上为示例xml文本
1.QXmlStreamWriter写xml
首先按照xml文件路径打开文件,为了容错考虑,若按照路径找不到文件,就创建文件
QFile file(path);
if(!file.open(QIODevice::WriteOnly))
return false;
创建QXmlStreamWriter实例
QXmlStreamWriter w(&file);
设置自动格式化输出的XML内容,设置为true时,将在适当的地方添加额外的空白字符(比如换行符和空格),使得生成的 XML 文件具有更好的可读性。
w.setAutoFormatting(true);
在 XML 文档的开始写入一个 XML 声明,也即是示例文本的第一行
w.writeStartDocument();
//写入 <?xml version="1.0" encoding="UTF-8"?>
w.writeEndDocument();
这个函数是整体xml内容的最大框架,相当于json文本的最外围大括号,虽然在xml文本的表现上只有第一行内容,但函数使用依然需要开始和结束对应。
开始写入内容,xml格式的写入与json格式写入很相似,通过writeStartElement和writeEndElement函数创建节点元素,通过writeTextElement函数写文本元素
xml文本内容中,尖括号中的内容为元素名,开始与结束相对应
<Soft> //节点元素 开始
<Name>Soft1</Name> //文本元素
</Soft> //节点元素 结束
因此,writeStartElement函数和writeEndElement函数应当对应使用,例如:
w.writeStartElement("Soft"); //<Soft>
...
w.writeEndElement(); //</Soft>
writeStartElement函数的形参为节点元素的名称。
文本元素使用writeTextElement函数写入,函数有两个形参,第一个是文本元素的名称,第二个是文本元素的内容:
w.writeTextElement("Name","Soft1");
//<Name>Soft1</Name>
了解了节点元素和文本元素的写入方法,就可以对照xml文本格式编码,最后记得要关闭文件。
因此,按照实例文本内容,写入代码如下:
QFile file(path);
if(!file.open(QIODevice::WriteOnly))
return false;
QXmlStreamWriter w(&file);
w.setAutoFormatting(true);
//<?xml version="1.0" encoding="UTF-8"?>
w.writeStartDocument();
w.writeStartElement("Soft"); //<Soft>
w.writeTextElement("Name","Soft1"); //<Name>Soft1</Name>
w.writeTextElement("Author","xx"); //<Author>xx</Author>
w.writeTextElement("Version","v1.0");//<Version>v1.0</Version>
w.writeTextElement("Explain","test");//<Explain>test</Explain>
w.writeStartElement("Scans"); //<Scans>
for(int j = 0 ; j < 1 ; j++)//原本是数组循环
{
w.writeStartElement("Scan"); //<Scan>
w.writeTextElement("Name","untitled2.ui");
//<Name>untitled2.ui</Name>
w.writeTextElement("SavePath","/home/MainProject/untitled2.ui");
//<SavePath>/home/MainProject/untitled2.ui</SavePath>
w.writeEndElement(); //</Scan>
}
w.writeEndElement(); //</Scans>
w.writeEndElement(); //</Soft>
w.writeEndDocument();
file.close(); //关闭文件
2.QXmlStreamReader读xml
按照xml文件内容,打开文件读入QXmlStreamReader实例中
QFile file(path);
if(!file.open(QIODevice::ReadOnly | QIODevice::Text))
return false;
QXmlStreamReader r(&file);
跳过xml文本第一行,这是为了跳过XML声明,不进行解析
r.readNext(); //跳过<?xml version="1.0" encoding="UTF-8"?>
现在开始xml文本内容解析,首先记录基础的函数功能:
r.readNext();//读下一节点
r.atEnd(); //判断当前是否处于结束位置
r.name(); //获取当前元素的名称
r.readElementText();//获取当前元素的文本内容
r.isStartElement();//判断当前是否处于元素开始位置
r.isEndElement();//判断当前是否处于元素结束位置
开始解析,可以想象QXmlStreamReader实例读取xml文本的光标最开始指向第一行,当跳过首行的xml声明后,光标指向第二行(以下代码的“|”指光标,之后代码块相同)
<?xml version="1.0" encoding="UTF-8"?>
|<Soft>
<Name>Soft1</Name>
<Author>xx</Author>
<Version>v1.0</Version>
<Explain>test</Explain>
<Scans>
<Scan>
<Name>untitled2.ui</Name>
<SavePath>/home/MainProject/untitled2.ui</SavePath>
</Scan>
</Scans>
</Soft>
进行循环,依次读取下一节点
while(!r.atEnd())
{
r.readNext();
...
}
在循环中,通过readNext()函数读入下一节点,等待读入,判断当前节点的类型是元素开始节点,再判定节点名称是否是"Soft",若是,再进行下一步处理;与之对应的,当此层的解析轮到该元素结束节点,需要跳出循环。
if(r.isStartElement()) //元素开始节点<Soft>
{
if(r.name().toString() == "Soft") //节点名称判定
{
...
}
else //若该元素名称错误,说明整个xml内容有问题
return false;
}
else if(r.isEndElement()) //元素结束节点,跳出循环
break;
此时,xml文本读取光标位置如下:
<?xml version="1.0" encoding="UTF-8"?>
<Soft>
|<Name>Soft1</Name>
<Author>xx</Author>
<Version>v1.0</Version>
<Explain>test</Explain>
<Scans>
<Scan>
<Name>untitled2.ui</Name>
<SavePath>/home/MainProject/untitled2.ui</SavePath>
</Scan>
</Scans>
</Soft>
需要依次读入文本元素内容,开始循环读取判定,判断当前读取节点是否为开始节点,若是,判定该节点名称,并作对应处理;对应的,若当前读入为结束节点,应跳出循环
while(!r.atEnd())
{
r.readNext();
if(r.isStartElement()) //元素开始节点
{
...
}
else if(r.isEndElement()) //结束节点 </Soft>
{
break;
}
}
若当前读入为开始节点,说明读取的是正常需要的数据内容。
解析当前节点名称,并获取节点文本内容,另外,若当前读入节点名称为"Scans",说明光标挪到了位置,此节点不是文本元素节点,需要另作处理
if(r.name().toString() == "Name") //节点名称判定
data.Name = r.readElementText(); //获取节点文本内容
else if(r.name().toString() == "Author")
data.Author = r.readElementText();
else if(r.name().toString() == "Version")
data.Version = r.readElementText();
else if(r.name().toString() == "Explain")
data.Explain = r.readElementText();
else if(r.name().toString() == "Scans")
{
...
}
此时读取的光标位置如下:
<?xml version="1.0" encoding="UTF-8"?>
<Soft>
<Name>Soft1</Name>
<Author>xx</Author>
<Version>v1.0</Version>
<Explain>test</Explain>
<Scans>
|<Scan>
<Name>untitled2.ui</Name>
<SavePath>/home/MainProject/untitled2.ui</SavePath>
</Scan>
</Scans>
</Soft>
这里是数组形式的结构,可能出现多个节点元素。
重新启动循环,判定一次读入的节点是否为元素开始节点,若是,说明当前读入一个节点元素,应进入进行另外的处理;与之对应的,读入元素结束节点当跳出循环
while(!r.atEnd())
{
r.readNext();
if(r.isStartElement())
{
if(r.name().toString() == "Scan")
{
...
}
else
return false;
}
else if(r.isEndElement())
{
break;
}
}
此时,光标位置如下:
<?xml version="1.0" encoding="UTF-8"?>
<Soft>
<Name>Soft1</Name>
<Author>xx</Author>
<Version>v1.0</Version>
<Explain>test</Explain>
<Scans>
<Scan>
|<Name>untitled2.ui</Name>
<SavePath>/home/MainProject/untitled2.ui</SavePath>
</Scan>
</Scans>
</Soft>
到此,其实步骤和最开始的节点解析一致了,每一个节点元素的解析步骤和最开始的节点元素解析类似。
于是,实例xml文本的解析代码如下:
QFile file(path);
if(!file.open(QIODevice::ReadOnly | QIODevice::Text))
return false;
QXmlStreamReader r(&file);
r.readNext(); //跳过首行
while(!r.atEnd())
{
r.readNext();
if(r.isStartElement())
{
if(r.name().toString() == "Soft")
{
while(!r.atEnd())
{
r.readNext();
if(r.isStartElement())
{
if(r.name().toString() == "Name")
data.Name = r.readElementText();
else if(r.name().toString() == "Author")
data.Author = r.readElementText();
else if(r.name().toString() == "Version")
data.Version = r.readElementText();
else if(r.name().toString() == "Explain")
data.Explain = r.readElementText();
else if(r.name().toString() == "Scans")
{
while(!r.atEnd())
{
r.readNext();
if(r.isStartElement())
{
if(r.name().toString() == "Scan")
{
ScanInfoDef Scan;
while(!r.atEnd())
{
r.readNext();
if(r.isStartElement())
{
if(r.name().toString() == "Name")
Scan.Name = r.readElementText();
else if(r.name().toString() == "SavePath")
Scan.SavePath = r.readElementText();
}
else if(r.isEndElement())
{
data.Scans.push_back(Scan);
break;
}
}
}
else
return false;
}
else if(r.isEndElement())
break;
}
}
}
else if(r.isEndElement())
break;
}
}
else
return false;
}
else if(r.isEndElement())
break;
}
file.close();