Qt 三种解析xml的方式

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

先给出xml如下:

 1 
 2 
 3     
 4         
 5             30
 6             soccer
 7         
 8         
 9             32
10             swimming
11         
12     
13     
14         
15             20
16             dancing
17         
18         
19             21
20             running
21         
22     
23 

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

本文福利,费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QT嵌入式开发,Quick模块,面试题等等)↓↓↓↓↓↓见下面↓↓文章底部点击费领取↓↓

 1 #include 
 2 #include 
 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 
 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 m_teachers;
23     std::vector m_students;
24 };
  1 #include "XmlStreamReader.h"
  2 #include 
  3 #include 
  4 #include 
  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 
 2 #include 
 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 m_teachers;
21     std::vector m_students;
22 };
  1 #include "domparser.h"
  2 #include 
  3 #include 
  4 #include 
  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 
 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 m_teachers;
27     std::vector m_students;
28 };
  1 #include "saxhandler.h"
  2 #include 
  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的方式。

本文福利,费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QT嵌入式开发,Quick模块,面试题等等)↓↓↓↓↓↓见下面↓↓文章底部点击费领取↓↓

你可能感兴趣的:(QT开发,qt,xml,c++,qt教程,qt开发)