Qt浅谈之二十八解析XML文件

一、简介

        QtXml模块提供了一个读写XML文件的流,解析方法包含DOM和SAX。DOM(Document ObjectModel):将XML文件表示成一棵树,便于随机访问其中的节点,但消耗内存相对多一些。SAX(Simple APIfor XML):一种事件驱动的XML API,接近于底层,速度较快,但不便于随机访问任意节点。    
       使用XML模块,在.pro文件中添加QT += xml,并加如相应的头文件#include <QDomDocument>或者#include <QXmlStreamReader>。
       分析的xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<COMMAND>
    <OBJECT>USER</OBJECT>
    <ACTION>LOGIN</ACTION>
    <DATA>
        <USER NAME="root" PASSWORD="123456"/>
    </DATA>
</COMMAND>

 

二、详解

1、QXmlStreamReader

(1)streamparsexml.h

 

#ifndef STREAMPARSEXML_H
#define STREAMPARSEXML_H
#include <QXmlStreamWriter>
#include <QXmlStreamReader>
#include <QFile>
#include <QMessageBox>

class StreamParseXml
{
public:
    StreamParseXml();
    ~StreamParseXml();
    int writeXml();
    int readXml();

private:
    void parseUserInformation();
    QString getValue(const QString &name);
    QString getAttribute(const QString &name);

private:
    QString fileName;
    QXmlStreamReader *reader;
};

#endif // STREAMPARSEXML_H

 

(2)streamparsexml.cpp

 

#include <QDebug>
#include "streamparsexml.h"

StreamParseXml::StreamParseXml()
{
    fileName = "streamparse.xml";
}

StreamParseXml::~StreamParseXml()
{

}

int StreamParseXml::writeXml()
{
    QFile file(fileName);
    if(file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        QXmlStreamWriter writer(&file);
        writer.setAutoFormatting(true);
        writer.writeStartDocument();
        writer.writeStartElement("COMMAND");
        writer.writeTextElement("OBJECT", "USER");
        writer.writeTextElement("ACTION", "LOGIN");
        writer.writeStartElement("DATA");
        writer.writeStartElement("USER");
        writer.writeAttribute("NAME", "root");
        writer.writeAttribute("PASSWORD", "123456");
        writer.writeEndElement();
        writer.writeEndElement();
        writer.writeEndElement();
        file.close();
    }
    return 0;
}

int StreamParseXml::readXml()
{
    if(fileName.isEmpty()) return -2;
    QFile *file = new QFile(fileName);
    if(!file->open(QFile::ReadOnly | QFile::Text)) {
        QMessageBox::information(NULL, QString("title"), QString("open error!"));
        return -1;
    }
    reader = new QXmlStreamReader(file);
     while(!reader->atEnd() && !reader->hasError()) {
         QXmlStreamReader::TokenType token = reader->readNext();
         if(token == QXmlStreamReader::StartDocument) {
             continue;
         }
          if (reader->isStartElement() && reader->name() == "OBJECT") {
              QString elementText = reader->readElementText();
              if (elementText == "USER") {
                  parseUserInformation();
                  break;
              }
          }
     }
    if (reader->hasError()) {
        qDebug() << reader->errorString();
        //QMessageBox::information(NULL, QString("parseXML"), reader->errorString());
    }
    reader->clear();
    delete reader;
    reader = NULL;
    return 0;
}

void StreamParseXml::parseUserInformation()
{
    QString elementString = getValue("ACTION");
    if (elementString == "LOGIN") {
        while(!reader->atEnd()) {
            reader->readNext();
            if (reader->name() == "USER") {
                QXmlStreamAttributes attributes = reader->attributes();
                if(attributes.hasAttribute("NAME")) {
                    qDebug() << "USER=" << attributes.value("NAME").toString();
                }
                if(attributes.hasAttribute("PASSWORD")) {
                    qDebug() << "PASSWORD=" << attributes.value("PASSWORD").toString();
                }
            }
        }
    }
}
QString StreamParseXml::getValue(const QString &name)
{
    while(!reader->atEnd()) {
        reader->readNext();
        if (reader->isStartElement() && reader->name() == name) {
            return reader->readElementText();
        }
    }
    return "";
}

(3)运行

 

 

 

2、QDomDocument

 

        QDomDocument类代表整个的XML文件。概念上讲:它是文档树的根节点,并提供了文档数据的基本访问方法。由于元素、文本节点、注释、指令执行等等不可能脱离一个文档的上下文,所以文档类也包含了需要用来创建这些对象的工厂方法。被创建的节点对象有一个ownerDocument()函数,它将对象与对象常见的文档上下文环境关联起来。DOM类中最常使用的是QDomNode、QDomDocument、QDomElement和QDomText。

        解析后的XML文件在内部是通过一个对象树来表示的,对象树可以使用各种QDom类进行访问。所有的QDom类只引用内部树上的对象。一旦最后一个DOM树的QDom对象和QDocument本身被删除掉时,DOM树上的所有内部对象会被删除掉。元素、文本节点等的创建是通过使用类提供的各种工厂方法完成的。使用QDom类的缺省构造函数只会生成空的对象,这些空的对象不能操作,也不能写入到文档中。

        QDomDocument::setContent()完成XML文档的设置,他从QFile对象中读取XML数据并检测XML文档的编码。setContent()有几种重载形式,可以分别从QByteArray、QString、QIODevice、QXmlInputSource中读取XML数据。
(1)domdocument.h

#ifndef DOMDOCUMENT_H
#define DOMDOCUMENT_H
#include <QDomDocument>
#include <QFile>
#include <QTextStream>

class DomDocument
{
public:
    DomDocument();
    ~DomDocument();
    int writeXml();
    int readXml();
    int readXml2();

private:
    QString fileName;
};

#endif // DOMDOCUMENT_H

(2)domdocument.cpp

#include <QDebug>
#include "domdocument.h"

DomDocument::DomDocument()
{
    fileName = "domparse.xml";
}

DomDocument::~DomDocument()
{

}

int DomDocument::writeXml()
{
    QFile file(fileName);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate))
        return -2;
    QTextStream out(&file);
    QDomDocument doc;
    QDomText text;
    QDomElement element;
    QDomAttr attr;
    QDomProcessingInstruction instruction;
    instruction = doc.createProcessingInstruction( "xml", "version = \'1.0\' encoding=\'UTF-8\'" );
    doc.appendChild( instruction );

    QDomElement root = doc.createElement( "COMMAND" );
    doc.appendChild(root);
    element = doc.createElement( "OBJECT" );
    text = doc.createTextNode( "USER" );
    element.appendChild(text);
    root.appendChild(element);

    element = doc.createElement( "ACTION" );
    text = doc.createTextNode( "LOGIN" );
    element.appendChild(text);
    root.appendChild(element);

    element = doc.createElement( "DATA" );
    root.appendChild(element);

    QDomElement userElement = doc.createElement( "USERINFO" );
    attr = doc.createAttribute( "NAME" );
    attr.setValue("root");
    userElement.setAttributeNode(attr);
    attr = doc.createAttribute( "PASSWORD" );
    attr.setValue("123456");
    userElement.setAttributeNode(attr);
    element.appendChild(userElement);

    doc.save(out, 4);       //each line space of file is 4
    return 0;
}

int DomDocument::readXml()
{
    QDomDocument doc;
    QFile file(fileName);
    QString error = "";
    int row = 0, column = 0;
    if (!file.open(QIODevice::ReadOnly)) return -2;

    if(!doc.setContent(&file, false, &error, &row, &column)){
        qDebug() << "parse file failed:" << row << "---" << column <<":" <<error;
        file.close();
        return -1;
    }

    file.close();
    QDomElement root = doc.documentElement();
    QDomNode node = root.firstChild();
    while(!node.isNull()) {
       QDomElement element = node.toElement(); // try to convert the node to an element.
       if(!element.isNull()) {
          qDebug()<<element.tagName() << ":" << element.text();
          QDomNode nodeson = element.firstChild();
          while(!nodeson.isNull()) {
              QDomElement elementson = nodeson.toElement();
              if(!elementson.isNull()) {
                  qDebug()<< "---" <<elementson.tagName();
                  if (elementson.hasAttribute("NAME")) {
                      qDebug()<< "---" << "NAME=" << elementson.attributeNode("NAME").value();
                  }
                  if (elementson.hasAttribute("PASSWORD")) {
                      qDebug()<< "---" << "PASSWORD=" << elementson.attributeNode("PASSWORD").value();
                  }
              }
              nodeson = nodeson.nextSibling();
          }
       }
       node = node.nextSibling();
    }
    return 0;
}
int DomDocument::readXml2()
{
    QDomDocument doc;
    QFile file(fileName);
    QString error = "";
    int row = 0, column = 0;
    if (!file.open(QIODevice::ReadOnly)) return -2;

    if(!doc.setContent(&file, false, &error, &row, &column)){
        qDebug() << "parse file failed:" << row << "---" << column <<":" <<error;
        file.close();
        return -1;
    }

    file.close();
    QDomElement root = doc.documentElement();
    QDomNode node = root.firstChildElement();
    while(!node.isNull()) {
       QDomElement element = node.toElement(); // try to convert the node to an element.
       if(!element.isNull()) {
           if (element.tagName() == "DATA") {
               qDebug()<< "---" <<element.tagName();
                QDomNodeList list = element.childNodes();
                for(int index = 0; index < list.count(); index++) {
                    QDomNode list_node = list.item(index);
                    QDomElement list_element = list_node.toElement();
                    if (list_element.hasAttribute("NAME")) {
                        qDebug()<< "---" << "NAME =" << list_element.attributeNode("NAME").value();
                    }
                    if (list_element.hasAttribute("PASSWORD")) {
                        qDebug()<< "---" << "PASSWORD =" << list_element.attributeNode("PASSWORD").value();
                    }
                }
           }
           else {
               qDebug()<<element.tagName() << ":" << element.text();
           }
       }
       node = node.nextSibling();
    }
    return 0;
}

(3)main.cpp

 

 

#include <QCoreApplication>
#include <QDebug>
#include "streamparsexml.h"
#include "domdocument.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
//    StreamParseXml stream;
//    stream.writeXml();
//    stream.readXml();

    DomDocument dom;
    dom.writeXml();
    dom.readXml();
    qDebug() << "***********************";
    dom.readXml2();

    return a.exec();
}

 

(4)运行

3、其他XML解析库

        常见C/C++ XML解析器有tinyxml、XERCES、squashxml、xmlite、pugxml、libxml等等,这些解析器有些是支持多语言的,有些只是单纯C/C++的。
(1)Xerces XML解析器
       官方网址:http://xerces.apache.org/xerces-c/
       Xerces前身是IBM的XML4C,XML4C也是一种功能强大的XML解析器,之后交给Apache基金会管理,遂改名为Xerces,Xerces-C++让你的程序提供读写XML数据更加容易,提供的共享库通过DOM、SAX、SAX2 API等方式对XML文档进行解析、生成、操作和验证。
       Xerces-C++忠实于XML 1.0建议和相关标准。
       Xerces-C++解析器高性能、模块化并且可扩展。相关开发资料也比较完善。
       除了C++版本,Xerces同时还提供Xerces Java,Xerces Perl等版本。
(2)TinyXML解析器
       官方网址:http://www.grinninglizard.com/tinyxml/
       TinyXML相比Xerces要功能简单些,正如其名Tiny,使用方法也比较简单,TinyXML也是一个开源的解析XML解析库,用于C++,支持Windows和Linux。TinyXML通过DOM模型遍历和分析XML。官方文档:
http://www.grinninglizard.com/tinyxmldocs/index.html
(3)squashXML解析器
        官方地址:http://ostatic.com/squashxml
        这个解析器在国内似乎少人使用,这个解析器也有些历史了。squashXML基于DOM Level2,也是一个XML轻量级的解析器。天缘之所以把这个写上是天缘比较看重这个解析器的目录划分及使用说明,易懂而且易上手。
(4)XMLBooster解析器
        官方网址:http://www.xmlbooster.com/
        XMLBooster开发关注点比较有特色,更加关注解析性能,声称:“Application integration of XML data cannot get any simpler or any faster: instead of dealing with sophisticated api (such as DOM or SAX), use a convenient data structure, generated to suit your specific purpose, in the language of your choice. ”。
        针对特殊需求使用更加方便的数据结构以提高性能。
(5)LibXML解析器
        官方地址:http://xmlsoft.org/
        LibXML本来是为Gnome项目开发(C开发),之后被广泛使用,功能非常强大,几乎适合于常见的所有操作系统下编译和开发使用。libxml++(地址:http://libxmlplusplus.sourceforge.net/)是对libxml XML解析器的C++封装版本。此外还有各种语言封装包,参考官方链接。
(6)补充:
        除了上述XML解析库外,还有一些XML解析器(参考:http://www.garshol.priv.no/xmltools/platform/cpp.html),比如Berkely DBXML(BDB)等,有兴趣的读者可自行Google搜索。
尽管XML解析器有很多种,而且功能差异很大,甚至是支持跨平台、多语言,但是对于你的应用而言,尽量选择一种相对熟悉、功能够用的即可,没必要去追求庞杂的解析器,我们只需关注:功能够用、相对稳定、适合扩展这三个功能即可。一旦有问题,修正和扩展都要更为容易。

四、总结

 

(1)注意:readElementText()函数在解析出开始标签时,就可以解析元素的文本了,直到遇到结束标签。readElementText()函数中好像有类似于readNext()这样的函数读取下一个元素并比较是否为endElement,所以当使用readElementText()读取完元素文本时,该元素的闭合标签已经被读走了。
(2)QDomProcessingInstruction instruction;instruction = doc.createProcessingInstruction("xml","version=/"1.0/" encoding=/"UTF-8/"");用来写入XML文件的声明,这对于一个XML文件来说不可缺少。
(3)源码已经打包上传到csdn上,可登录下载(http://download.csdn.net/detail/taiyang1987912/8857327)。
(4)若有建议,请留言,在此先感谢! 

 

  • 13
    点赞
  • 70
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乌托邦2号

博文不易,支持的请给予小小打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值