• Qt 三种解析xml的方式


    在下面的随笔中,我会根据xml的结构,给出Qt中解析这个xml的三种方式的代码。虽然,这个代码时通过调用Qt的函数实现的,但是,很多开源的C++解析xml的库,甚至很多其他语言解析xml的库,都和下面三种解析xml采用相同的原理,所以就算你不是学习qt,也可以大致参看一下代码,对三种解析方式有一种大致的感觉。

    先给出xml如下:

     1 <?xml version="1.0" encoding="utf-8"?>
     2 <school>
     3     <teacher>
     4         <entry name="Job">
     5             <age>30</age>
     6             <sport>soccer</sport>
     7         </entry>
     8         <entry name="Tom">
     9             <age>32</age>
    10             <sport>swimming</sport>
    11         </entry>
    12     </teacher>
    13     <student>
    14         <entry name="Lily">
    15             <age>20</age>
    16             <sport>dancing</sport>
    17         </entry>
    18         <entry name="Keith">
    19             <age>21</age>
    20             <sport>running</sport>
    21         </entry>
    22     </student>
    23 </school>

    下面给出qt中解析xml的三种方式,通过解析xml,创建student列表和teacher列表。先给出存储的结构体和辅助函数:

     1 #include <string>
     2 #include <ostream>
     3 
     4 namespace School
     5 {
     6 
     7 struct Teacher
     8 {
     9     std::string name;
    10     int age;
    11     std::string loveSport;
    12 
    13     Teacher(std::string name_, int age_, std::string loveSport_)
    14         : name(std::move(name_)), age(age_), loveSport(std::move(loveSport_))
    15     {
    16 
    17     }
    18 };
    19 
    20 struct Student
    21 {
    22     std::string name;
    23     int age;
    24     std::string loveSport;
    25 
    26     Student(std::string name_, int age_, std::string loveSport_)
    27         : name(std::move(name_)), age(age_), loveSport(std::move(loveSport_))
    28     {
    29 
    30     }
    31 };
    32 
    33 inline void print(std::ostream &out, const Teacher& teacher)
    34 {
    35     out << "teacher: " << teacher.name << std::endl;
    36     out << "\tage: " << teacher.age << std::endl;
    37     out << "\tfavorite sport: " << teacher.loveSport << std::endl;
    38 }
    39 
    40 inline void print(std::ostream& out, const Student& student)
    41 {
    42     out << "student: " << student.name << std::endl;
    43     out << "\tage: " << student.age << std::endl;
    44     out << "\tfavorite sport: " << student.loveSport << std::endl;
    45 }
    46 
    47 }

    另外需要注意在.pro中添加

    QT += xml

    (1)通过QXmlStreamReader:

     1 #include <QXmlStreamReader>
     2 #include "schooldefine.h"
     3 
     4 class XmlStreamReader
     5 {
     6 public:
     7     XmlStreamReader();
     8 
     9     bool readFile(const QString& fileName);
    10     void printAllMembers();
    11 
    12 private:
    13     void readSchoolMembers();
    14     void readTeacherMembers();
    15     void readTeacher(const QStringRef& teacherName);
    16     void readStudentMembers();
    17     void readStudent(const QStringRef& studentName);
    18     void skipUnknownElement();
    19 
    20     QXmlStreamReader reader;
    21 
    22     std::vector<School::Teacher> m_teachers;
    23     std::vector<School::Student> m_students;
    24 };
      1 #include "XmlStreamReader.h"
      2 #include <QFile>
      3 #include <iostream>
      4 #include <QDebug>
      5 
      6 XmlStreamReader::XmlStreamReader()
      7 {
      8 
      9 }
     10 
     11 bool XmlStreamReader::readFile(const QString &fileName)
     12 {
     13     QFile file(fileName);
     14     if (!file.open(QFile::ReadOnly | QFile::Text))
     15     {
     16         std::cerr << "Error: Cannot read file " << qPrintable(fileName)
     17                   << ": " << qPrintable(file.errorString())
     18                   << std::endl;
     19         return false;
     20     }
     21     reader.setDevice(&file);
     22 
     23     reader.readNext();
     24     while (!reader.atEnd())
     25     {
     26         if (reader.isStartElement())
     27         {
     28             if (reader.name() == "school")
     29             {
     30                 readSchoolMembers();
     31             }
     32             else
     33             {
     34                 reader.raiseError(QObject::tr("Not a school file"));
     35             }
     36         }
     37         else
     38         {
     39             reader.readNext();
     40         }
     41     }
     42 
     43     file.close();
     44     if (reader.hasError())
     45     {
     46         std::cerr << "Error: Failed to parse file "
     47                   << qPrintable(fileName) << ": "
     48                   << qPrintable(reader.errorString()) << std::endl;
     49         return false;
     50     }
     51     else if (file.error() != QFile::NoError)
     52     {
     53         std::cerr << "Error: Cannot read file " << qPrintable(fileName)
     54                   << ": " << qPrintable(file.errorString())
     55                   << std::endl;
     56         return false;
     57     }
     58     return true;
     59 }
     60 
     61 void XmlStreamReader::printAllMembers()
     62 {
     63     std::cout << "All teachers: " << std::endl;
     64     for (const auto& teacher : m_teachers)
     65     {
     66         School::print(std::cout, teacher);
     67     }
     68     std::cout << "All students: " << std::endl;
     69     for (const auto& student : m_students)
     70     {
     71         School::print(std::cout, student);
     72     }
     73 }
     74 
     75 void XmlStreamReader::readSchoolMembers()
     76 {
     77     reader.readNext();
     78     while (!reader.atEnd())
     79     {
     80         if (reader.isEndElement())
     81         {
     82             reader.readNext();
     83             break;
     84         }
     85 
     86         if (reader.isStartElement())
     87         {
     88             if (reader.name() == "teacher")
     89             {
     90                 readTeacherMembers();
     91             }
     92             else if (reader.name() == "student")
     93             {
     94                 readStudentMembers();
     95             }
     96             else
     97             {
     98                 skipUnknownElement();
     99             }
    100         }
    101         else
    102         {
    103             reader.readNext();
    104         }
    105     }
    106 }
    107 
    108 void XmlStreamReader::readTeacherMembers()
    109 {
    110     reader.readNext();
    111     while (!reader.atEnd())
    112     {
    113         if (reader.isEndElement())
    114         {
    115             reader.readNext();
    116             break;
    117         }
    118 
    119         if (reader.isStartElement())
    120         {
    121             if (reader.name() == "entry")
    122             {
    123                 readTeacher(reader.attributes().value("name"));
    124             }
    125             else
    126             {
    127                 skipUnknownElement();
    128             }
    129         }
    130         else
    131         {
    132             reader.readNext();
    133         }
    134     }
    135 }
    136 
    137 void XmlStreamReader::readTeacher(const QStringRef& teacherName)
    138 {
    139     reader.readNext();
    140 
    141     int age = 0;
    142     std::string favoriteSport;
    143 
    144     while (!reader.atEnd())
    145     {
    146         if (reader.isEndElement())
    147         {
    148             reader.readNext();
    149             break;
    150         }
    151 
    152         if (reader.isStartElement())
    153         {
    154             if (reader.name() == "age")
    155             {
    156                 age = reader.readElementText().toInt();
    157             }
    158             else if (reader.name() == "sport")
    159             {
    160                 favoriteSport = reader.readElementText().toStdString();
    161             }
    162             else
    163             {
    164                 skipUnknownElement();
    165             }
    166         }
    167         reader.readNext();
    168     }
    169 
    170     m_teachers.emplace_back(teacherName.toString().toStdString(), age, favoriteSport);
    171 }
    172 
    173 void XmlStreamReader::readStudentMembers()
    174 {
    175     reader.readNext();
    176     while (!reader.atEnd())
    177     {
    178         if (reader.isEndElement())
    179         {
    180             reader.readNext();
    181             break;
    182         }
    183 
    184         if (reader.isStartElement())
    185         {
    186             if (reader.name() == "entry")
    187             {
    188                 readStudent(reader.attributes().value("name"));
    189             }
    190             else
    191             {
    192                 skipUnknownElement();
    193             }
    194         }
    195         else
    196         {
    197             reader.readNext();
    198         }
    199     }
    200 }
    201 
    202 void XmlStreamReader::readStudent(const QStringRef &studentName)
    203 {
    204     reader.readNext();
    205 
    206     int age = 0;
    207     std::string favoriteSport;
    208 
    209     while (!reader.atEnd())
    210     {
    211         if (reader.isEndElement())
    212         {
    213             reader.readNext();
    214             break;
    215         }
    216 
    217         if (reader.isStartElement())
    218         {
    219             if (reader.name() == "age")
    220             {
    221                 age = reader.readElementText().toInt();
    222             }
    223             else if (reader.name() == "sport")
    224             {
    225                 favoriteSport = reader.readElementText().toStdString();
    226             }
    227             else
    228             {
    229                 skipUnknownElement();
    230             }
    231         }
    232         reader.readNext();
    233     }
    234 
    235     m_students.emplace_back(studentName.toString().toStdString(), age, favoriteSport);
    236 }
    237 
    238 void XmlStreamReader::skipUnknownElement()
    239 {
    240     reader.readNext();
    241     while (!reader.atEnd())
    242     {
    243         if (reader.isEndElement())
    244         {
    245             reader.readNext();
    246             break;
    247         }
    248 
    249         if (reader.isStartElement())
    250         {
    251             skipUnknownElement();
    252         }
    253         else
    254         {
    255             reader.readNext();
    256         }
    257     }
    258 }

    (2)通过DOM方式:

     1 #include <QString>
     2 #include <QDomElement>
     3 #include "schooldefine.h"
     4 
     5 class DomParser
     6 {
     7 public:
     8     DomParser();
     9 
    10     bool readFile(const QString &fileName);
    11     void printAllMembers();
    12 
    13 private:
    14     void parseSchoolMembers(const QDomElement &element);
    15     void parseTeacherMembers(const QDomElement &element);
    16     void parseStudentMembers(const QDomElement &element);
    17     void parseTeacher(const QDomElement &element);
    18     void parseStudent(const QDomElement &element);
    19 
    20     std::vector<School::Teacher> m_teachers;
    21     std::vector<School::Student> m_students;
    22 };
      1 #include "domparser.h"
      2 #include <QDomDocument>
      3 #include <QFile>
      4 #include <iostream>
      5 
      6 DomParser::DomParser()
      7 {
      8 
      9 }
     10 
     11 bool DomParser::readFile(const QString &fileName)
     12 {
     13     QFile file(fileName);
     14     if (!file.open(QFile::ReadOnly | QFile::Text)) {
     15         std::cerr << "Error: Cannot read file " << qPrintable(fileName)
     16                   << ": " << qPrintable(file.errorString())
     17                   << std::endl;
     18         return false;
     19     }
     20 
     21     QString errorStr;
     22     int errorLine;
     23     int errorColumn;
     24 
     25     QDomDocument doc;
     26     if (!doc.setContent(&file, false, &errorStr, &errorLine, &errorColumn))
     27     {
     28         std::cerr << "Error: Parse error at line " << errorLine << ", "
     29                   << "column " << errorColumn << ": "
     30                   << qPrintable(errorStr) << std::endl;
     31         return false;
     32     }
     33 
     34     QDomElement root = doc.documentElement();
     35     if (root.tagName() != "school")
     36     {
     37         std::cerr << "Error: Not a school file" << std::endl;
     38         return false;
     39     }
     40 
     41     parseSchoolMembers(root);
     42     return true;
     43 }
     44 
     45 void DomParser::printAllMembers()
     46 {
     47     std::cout << "All teachers: " << std::endl;
     48     for (const auto& teacher : m_teachers)
     49     {
     50         School::print(std::cout, teacher);
     51     }
     52     std::cout << "All students: " << std::endl;
     53     for (const auto& student : m_students)
     54     {
     55         School::print(std::cout, student);
     56     }
     57 }
     58 
     59 
     60 void DomParser::parseSchoolMembers(const QDomElement &element)
     61 {
     62     QDomNode child = element.firstChild();
     63     while (!child.isNull())
     64     {
     65         if (child.toElement().tagName() == "teacher")
     66         {
     67             parseTeacherMembers(child.toElement());
     68         }
     69         else if (child.toElement().tagName() == "student")
     70         {
     71             parseStudentMembers(child.toElement());
     72         }
     73         child = child.nextSibling();
     74     }
     75 }
     76 
     77 void DomParser::parseTeacherMembers(const QDomElement &element)
     78 {
     79     QDomNode child = element.firstChild();
     80     while (!child.isNull())
     81     {
     82         if (child.toElement().tagName() == "entry")
     83         {
     84             parseTeacher(child.toElement());
     85         }
     86         child = child.nextSibling();
     87     }
     88 }
     89 
     90 void DomParser::parseStudentMembers(const QDomElement &element)
     91 {
     92     QDomNode child = element.firstChild();
     93     while (!child.isNull())
     94     {
     95         if (child.toElement().tagName() == "entry")
     96         {
     97             parseStudent(child.toElement());
     98         }
     99         child = child.nextSibling();
    100     }
    101 }
    102 
    103 void DomParser::parseTeacher(const QDomElement &element)
    104 {
    105     auto children = element.childNodes();
    106     auto firstChild = children.at(0).toElement();
    107     auto secondChild = children.at(1).toElement();
    108     int age = firstChild.text().toInt();
    109 
    110     m_teachers.emplace_back(element.attribute("name").toStdString(),
    111                 age, secondChild.text().toStdString());
    112 }
    113 
    114 void DomParser::parseStudent(const QDomElement &element)
    115 {
    116     auto children = element.childNodes();
    117     auto firstChild = children.at(0).toElement();
    118     auto secondChild = children.at(1).toElement();
    119     int age = firstChild.text().toInt();
    120 
    121     m_students.emplace_back(element.attribute("name").toStdString(),
    122                 age, secondChild.text().toStdString());
    123 }

    3. 采用QXmlSimpleReader方式,也就是回调函数方式:

     1 #include <QXmlDefaultHandler>
     2 #include "schooldefine.h"
     3 
     4 class SaxHandler : public QXmlDefaultHandler
     5 {
     6 public:
     7     SaxHandler();
     8 
     9     bool readFile(const QString &fileName);
    10     void printAllMembers();
    11 
    12 protected:
    13     bool startElement(const QString &namespaceURI,
    14                       const QString &localName,
    15                       const QString &qName,
    16                       const QXmlAttributes &atts) override;
    17     bool endElement(const QString &namespaceURL,
    18                     const QString &localName,
    19                     const QString &qName) override;
    20     bool characters(const QString &ch) override;
    21     bool fatalError(const QXmlParseException &exception) override;
    22 
    23 private:
    24     bool m_isStudent = false;
    25     QString  m_currentContext;
    26     std::vector<School::Teacher> m_teachers;
    27     std::vector<School::Student> m_students;
    28 };
      1 #include "saxhandler.h"
      2 #include <iostream>
      3 
      4 SaxHandler::SaxHandler()
      5 {
      6 
      7 }
      8 
      9 bool SaxHandler::readFile(const QString &fileName)
     10 {
     11     QFile file(fileName);
     12     QXmlInputSource inputSource(&file);
     13     QXmlSimpleReader reader;
     14     reader.setContentHandler(this);
     15     reader.setErrorHandler(this);;
     16     return reader.parse(inputSource);
     17 }
     18 
     19 void SaxHandler::printAllMembers()
     20 {
     21     std::cout << "All teachers: " << std::endl;
     22     for (const auto& teacher : m_teachers)
     23     {
     24         School::print(std::cout, teacher);
     25     }
     26     std::cout << "All students: " << std::endl;
     27     for (const auto& student : m_students)
     28     {
     29         School::print(std::cout, student);
     30     }
     31 }
     32 
     33 bool SaxHandler::startElement(const QString &namespaceURI,
     34                               const QString &localName,
     35                               const QString &qName,
     36                               const QXmlAttributes &atts)
     37 {
     38     if (qName == "teacher")
     39     {
     40         m_isStudent = false;
     41     }
     42     else if (qName == "student")
     43     {
     44         m_isStudent = true;
     45     }
     46     else if (qName == "entry")
     47     {
     48         if (m_isStudent)
     49         {
     50             m_students.push_back(School::Student("", 0, ""));
     51             m_students.back().name = atts.value("name").toStdString();
     52         }
     53         else
     54         {
     55             m_teachers.push_back(School::Teacher("", 0, ""));
     56             m_teachers.back().name = atts.value("name").toStdString();
     57         }
     58     }
     59     else if (qName == "age")
     60     {
     61         m_currentContext.clear();
     62     }
     63     else if (qName == "sport")
     64     {
     65         m_currentContext.clear();
     66     }
     67     return true;
     68 }
     69 
     70 bool SaxHandler::characters(const QString &ch)
     71 {
     72     m_currentContext += ch;
     73     return true;
     74 }
     75 
     76 bool SaxHandler::endElement(const QString &namespaceURL,
     77                             const QString &localName,
     78                             const QString &qName)
     79 {
     80     if (qName == "age")
     81     {
     82         if (m_isStudent)
     83         {
     84             m_students.back().age = m_currentContext.toInt();
     85         }
     86         else
     87         {
     88             m_teachers.back().age = m_currentContext.toInt();
     89         }
     90     }
     91     else if (qName == "sport")
     92     {
     93         if (m_isStudent)
     94         {
     95             m_students.back().loveSport = m_currentContext.toStdString();
     96         }
     97         else
     98         {
     99             m_teachers.back().loveSport = m_currentContext.toStdString();
    100         }
    101     }
    102     m_currentContext.clear();
    103     return true;
    104 }
    105 
    106 bool SaxHandler::fatalError(const QXmlParseException &exception)
    107 {
    108     std::cerr << "Parse error at line" << exception.lineNumber()
    109               << ", " << "column " << exception.columnNumber() << ": "
    110               << qPrintable(exception.message()) << std::endl;
    111     return false;
    112 }

    下面简单对上述三种方式予以说明:

    (1) 从代码行数来看,采用DOM和QXmlSimpleReader的方式,代码行数比较少,而QXmlStreamReader代码行数较多。

    (2) 从代码逻辑分析来看,采用DOM方式最容易理解,采用QXmlStreamReader的方式稍微难理解一些,而采用QXmlSimpleReader由于使用了较多的回调,引入了大量的类数据成员,使得代码会很难理解。

    (3) 从内存占用来看,DOM的方式会耗费最多的内存,因为需要一次性将所有的内容构建成树,DOM和QXmlSimpleReader对内存要求都较低。

    (4) 从运行时间消耗来看,DOM的消耗,可能会稍微大一些,因为DOM正常要经历2次的遍历,一次遍历构建树,一次遍历,构建自己需要的数据。而QXmlSimpleReader和QXmlStreamReader正常只需要遍历一次。

    (5) 从处理异常来看,DOM和QXmlStreamReader应该会更容易一些,因为不涉及回调函数,但是对于xml来说,很多时候主要确认内容正确与否,如果错误就退出,查看xml中的错误。当然,这个也是比较重要的项。

    对于我来说,因为大多数情况下,解析的xml不是很大,而且基本只涉及加载过程中,所以使用DOM的情况比较多。如果xml比较大,或者调用比较频繁,可以考虑使用QXmlStreamReader的方式。

  • 相关阅读:
    zoj1942Frogger
    阿里云api调用做简单的cmdb
    约瑟夫环
    Bag标签之中的一个行代码实行中文分词实例3
    用lua扩展你的Nginx(整理)
    Single Number III
    TCP/IP ---分层
    TCP/IP --概述
    adb命令学习
    How to set Selenium Python WebDriver default timeout?
  • 原文地址:https://www.cnblogs.com/ybqjymy/p/15874159.html
Copyright © 2020-2023  润新知