在下面的随笔中,我会根据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模块,面试题等等)↓↓↓↓↓↓见下面↓↓文章底部点击莬费领取↓↓