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的方式。


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM