How-to: parsing XML with Qt
How-to: parsing XML with Qt Why use XML? The purpose of this document is to show a couple ways oftransforming data from a saved state into useful objects in memory,on the Boardcon 2440. Thedocument type best suited to this task is XML, and as the Boardcon 2440 mark III sports a Qt-everywhereimplementation,which includes QTs XML parsing objects,parsing XML with QT will be the subjectof this paper. The advantage of XML over a regular csv file,or customcharacter or binary file is that “records” or “elements” can be different from one another whilestill being parsed by the same code. In designing a trivia game,if you decided you needed a Questionobject,which contained question text,several different answer texts and anindication of which answer is correct,and you decided to do this with a csv file,your data might looklike this: 1,What color is the sky?,red,green,blue,purple,2, 2,What is the air speed velocity of anAfrican swallow? 24mph,10 mph,5Mm/h,0 3,What’s MontyPython? An English comedy group,a particularly scary snake,A dishwasher brand,0 … using a file stream reader and tokenizing on stringswith a comma “,” as the delimiter. But this is problematic,as adding any of the followingwould require lots of additional development: · Support for questions with several answers · Support for questions with external references orresources · Support for comments Using the XML alleviates most of these problems. <Question ID=”1” CorrectAnsID=”3” > <QuestionText>What color is thesky?</QuestionText> <Answer ID=”1”>red</Answer> <Answer ID=”2”>green</Answer> <Answer ID=”3”>Blue</Answer> <Answer ID=”4”>Purple</Answer> </Question> <Question ID=”2” CorrectAnsID=”1” > <QuestionText> What is the air speedvelocity of an African swallow?</QuestionText> <Answer ID=”1”>24mph</Answer> <Answer ID=”2”>5mph</Answer> <Answer ID=”3”>5Mm/h</Answer> <!-- that’sMega-meters per second,and this is a comment,which the parser will automatically handle forus.--> <Answer ID=”4”>Purple</Answer> </Question> <!-- below is an example of somefunctionality that could be added, even though this snippit is still valid XMLand could conceivably be used by the same code --> <Question ID=”2” CorrectAnsID=”1” Type=”MultiMedia”> <Resource Type=”AUDIO”>monty-airspeed.mp3</Resource> <Resource Type=”IMAGE”>monty-swallow.jpg</Resource> <QuestionText> What’s Monty Python ? </QuestionText> <Answer ID=”1”>24mph</Answer> <Answer ID=”2”>5mph</Answer> <Answer ID=”3”>5Mm/h</Answer> <!-- that’sMega-meters per second,and this is a comment --> <Answer ID=”4”>Purple</Answer> </Question> Qt is a sophisticated high-level framework on top of C++,as such it provides several objects to access elements embedded in an XML file. It includes acomplete implementation as per the W3C’s Document Object Model (“DOM”) called QDomDocument,but italso includes an easier to use QXmlStreamReader,which abstracts away from the siblingtree-like nature of XML and just gives you a serialized view. Writing your XML document General resources: · On linux,gedit provides basic XML highlighting if theextension is xml. · On windows,notepad++ can be used to do the same. · The XML plugin to eclipse does not provide highlighting,it abstracts further,into a 2-column table with drop down menus. · XML validators. Use this to ensure your XML document isvalid: o http://www.w3schools.com/xml/xml_validator.asp Writing the Qt XML parsingcode Open a terminal,and change to your projects directory: >cd~/cmpt433/YourUserName/private/myApps/YourProject Adding support for XML to yourproject First,modify your projects .pro to indicate to the QTpre-compiler that you’ll need XML parsing objects: >gedit ./yourProject.pro #------------------------------------------------- # # Project created by QtCreator2011-11-12T15:31:45 # #------------------------------------------------- QT += core gui xml TARGET = YourProject ... Including the needed Libraries Include the libraries “QXmlStreamReader “,“QFile”,and “QDir”in the header file of the object that’s going to be reading the XML. You may not need the lattertwo,but they are very helpful. In your code editor of choice,or with gedit: >gedit ./yourXMLParsingObject.h #include <QObject> #include <QDebug> #include<QXmlStreamReader> #include <QFile> #include <QDir> class YourXMLParsingObject ... Get the XML file to parse Navigating directories can be tricky,though it is notthe subject of this how-to,the QDir and QFile objects can aide in this. If your application can bethought of as a shell then a QDir object can be thought of as the present-working-directory. Some helpful methodsto note: · QDir.AbsolutePath() gets the fully qualified path to thedirectory its currently examining. · QDir.cdUp() moves the QDir object up one level in thedirectory tree · QDir.cd(Qstring dirName) moves the QDir down one level inthe directory tree to the specified sub-directory To allow your application to run on both your target,andany windows or linux development environments,It’s also helpful to use a constant staticfield or a #define to hold the path of the file,and a precompiler switch to determine where the file is inyour directory structure. Modify yourXMLParsingObject.h again toinclude this statement: class Model ... private: const QString xmlFilePath; ... Followed by modifying the yourXMLParsingObject.cppfile to set that variable,machine dependent. Unfortunately,because of the nature of C++’shandling of constant object field references this is going to get ugly,but it does give the desiredeffect: #include "model.h" //constructor yourXMLParsingObject::yourXMLParsingObject( QObject *parent) : QObject(parent),AnyDependentObjects(), #ifdef __arm__ //on the target xmlFilePath("/mnt/remote/YourProject/myXML.xml") #else #ifdef __WIN32 //for those developing onwindows xmlFilePath("C:UsersYouQTYourProjectmyXML.xml") #else //for those developing on linux xmlFilePath("/home/You/QT/YourProject/myXML.xml") #endif #endif { score = 0; lives = 4; ... Some notes · Because Qt on Windows uses mingw and some ported linuxlibraries,finding a defined constant to use as a precompiler #ifdef constantis difficult. As of writing,I have not found one. · If you wish to add further dependencies after this one,put a comma after each of the xmlfilePath constructor calls (read: turn “xmlFilePath(…)” into “xmlFilePath(…),” for each of them) then any additionaldependencies you need after the last #endif. · Using the non-escape character for a file path on windows(IE using “/” instead of “”) will work,but windows traditionally handles file paths with a“” rather than a “/”,so convention is why “” is used here. Parsing the XML file Given the XML file: <?xml version="1.0"encoding="UTF-8"?> <Quiz> <GlobalVar ID="ODE2C0DE">coolValue with Digits 2.45</GlobalVar> <QuestionSet> <Question ID="1"> <Text>What is your favoriteColor?</Text> <Answer>Blue</Answer> </Question> <Question ID="2"> <Text>How much Do I loveQT</Text> <Answer>At 10:34PM on Friday,not awhole lot</Answer> </Question> </QuestionSet> </Quiz> Below is known working code with comments explaining thebehavior: //Assuming somewhere we have a questionobject,similar to:
Struct GlobalVarADT{ int ID; QString text; GlobalVarADT globalVar; Struct Question{ int questionID; QString questionText; QString answerText; } QList<Question> questions; //This is going to be a member function ofyourXMLParsingObject //that takes a filePath and reads each “question” element into a //QList of question objects. voidYourXMLParsingObject::yourXMLParsingMethod(QString filePath){ //create a new file object with the XML filepath. QFile* file = new QFile(xmlFilePath); //test to see if the file is readable andthat it contains text. if(!file->open(QIODevice::ReadOnly |QIODevice::Text)){ //if you wish to perform some action if thefile is inaccessible,//do so here. return; } //Create the XML stream reader. QXmlStreamReader xml(file); //we’re going toloop over the entire xml document //using QXmlStreamReader’s atEnd() method,in addition to //its hasError() method while(!xml.atEnd() &&!xml.hasError()){ //read the next piece of data into thereader. //this will move the reader “over” or “onto” the next //valid element,including any attributes itholds as //well as its value. The returned object is astatus object //which can be tested for EOF,the begging ofthe file,etc. QXmlStreamReader::TokenType token =xml.readNext(); //test to see if the token indicates that we’re at the beginning //of the document. Any calls to the reader’s current data will //give you the xml version numberinformation. if(token == QXmlStreamReader::StartDocument){ //we don’t want anyof this data,it isn’t any element //we need. continue; } //what we’re lookingfor is that start of a valid element if(token == QXmlStreamReader::StartElement) { //any global elements or elements of quizthat you wish //to read in come next if(xml.name() == "GlobalVar"){ //here we’ve gotglobalVar,so I’m going to //save the ID attribute,and save its text tothe //globalVar globalVar.ID = xml.attributes().value(“ID”).toString().toInt(); //So your XML reader was looking at theGlobalVar //element,we need to push it forward (suchthat its //looking at the value field) so that itreads the //text of this current element. xml.readNext(); //now save the xml.text(),this will be thevalue in //the globalvar element (<GlobalVar>HERE</GlobalVar>) globalVar.text = xml.text().toString(); } //Questionset itself doesn’t do anything for us. We need its //children,so just skip this element if(xml.name() == QuestionSet){ continue; } //This is a question,in the name of goodsoftware //engineering I’m deligatingthis off to another //function called “parseQuestion”. if(xml.name() == "Question") { this->parseQuestion(xml); } }//startElement }//while }//function voidYourXMLParsingObject::parseQuestion(QXmlStreamReader& xml){ //check to ensure that we were called in theappropriate spot! if(xml.tokenType() !=QXmlStreamReader::StartElement && xml.name() !="Question"){ qDebug() << "Called XMLparseQuestionElement " << "without a question Element inthe XML stream!"; return; } //create some memory to read the fields into Question* newQuestion = new Question(); //read the attribute fields in first: Youknow that currently the //stream is pointing at the Question element(as was checked by //the previous if condition),so you can readin the attribute //fields immediately. newQuestion->ID =xml.attributes().value("ID").toString().toInt(); //but now we need to get data from Question'schildren... xml.readNext(); //and now the code looks very similar to thatof the caller! //the way to read the while condition is asfollows: //while it isn’t the casethat we're at the end of an element and //(to protect against nested questions) we'renot looking at a //new question element while(!(xml.tokenType() ==QXmlStreamReader::EndElement && xml.name() =="Question")){ //at the start of an element,otherwiseignore and //keep reading. if(xml.tokenType() ==QXmlStreamReader::StartElement){ //If the element is a text element,save it if(xml.name() == "Text"){ xml.readNext(); newQuestion->questionText = xml.text().toString(); } //otherwise if its an answer element,save it if(xml.name() == "Answer"){ xml.readNext(); newQuestion->answerText = xml.text().toString(); } //otherwise we don’t know what it is,so do nothing,//and read the next thing! } //we’re currentlylooking at some element feld,//so read the next element header! xml.readNext(); }//while //add the new question we just created to thelist. questions.add(newQuestion); //done! return; }from:http://www.cs.sfu.ca/CourseCentral/433/bfraser/other/2011-student-howtos/XML-in-Qt.pdf (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |