【记录】QT读写XML(QXmlStreamWriter/QXmlStreamReader的简单使用)

<?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();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值