掌握了ACMXML库解析XML文件的方法后,下面来实现一个比较完整的程序。
定义基本结构
xml文件格式如下
<?xml version="1.0"?> <root version="9" count="3" > <file id="1">D: est1.txt</file> <file id="2">D: est2.txt</file> <file id="3">D: est3.txt</file> </root>
这个xml文件虽然短小,但是对于示例程序来说已经足够了。
xml文件解析后,要将解析的数据保存起来,可以定义这样两个结构:
//对应xml文件的根节点 typedef struct _ROOT { //版本号 int version; //子项的数量 int count; } ROOT; //对应xml文件的子项 typedef struct _FILE { int id; //文件路径 string path; } ROOT_FILE;
通过宏定义来指定每个节点的名字:
#define XML_ROOT "root" #define XML_FILE "file" #define XML_ID "id" #define XML_VER "version" #define XML_COUNT "count"
定义并实现两个类xml_handler、xml_parser。
xml_parser负责解析xml文件,但是它并不关心xml文件中的数据按什么样的格式保存。
而和具体的格式相关的类为xml_handler,解析xml文件后的数据会保存在这个类的实例里面。
xml_hanlder主要方法介绍
通过startElement和characters两个方法来获取数据:
virtual void startElement( const ACEXML_Char *namespaceURI, const ACEXML_Char *localName, const ACEXML_Char *qName, ACEXML_Attributes *atts ) { //file节点 if(stricmp(localName, XML_FILE) == 0) { if(root_.count == files_.size()) { return; } flag_ = FLAG_FILE; ROOT_FILE* file = new ROOT_FILE(); files_.push_back(file); for(int i=0; i<atts->getLength(); ++i) { const char* qname = atts->getQName(i); if(stricmp(qname, XML_ID) == 0) { file->id = atoi(atts->getValue(i)); } } } //root节点 else if(stricmp(localName, XML_ROOT) == 0) { flag_ = FLAG_ROOT; for(int i=0; i<atts->getLength(); ++i) { const char* qname = atts->getQName(i); if(stricmp(qname, XML_VER) == 0) { root_.version= atoi(atts->getValue(i)); } else if(stricmp(qname, XML_COUNT) == 0) { root_.count = atoi(atts->getValue(i)); } } } } virtual void characters( const ACEXML_Char *ch, size_t start, size_t length ) { if(flag_ == FLAG_FILE) { files_[files_.size() - 1]->path = ch; } flag_ = FLAG_NULL; }
通过flush方法将root、file信息写入到xml文件中:
//将当前root、file信息写入到xml文件中 bool flush() const { ofstream ofs(filepath_.c_str(), ios_base::trunc); if(!ofs) { return false; } ofs << "<?xml version="1.0"?>" << endl; ofs << "<" << XML_ROOT << " " << XML_VER << "="" << root_.version << "" " << XML_COUNT << "="" << root_.count << "" >" << endl; for(size_t i=0; i<files_.size(); ++i) { ofs << " <" << XML_FILE << " " << XML_ID << "="" << files_[i]->id << "" >" << files_[i]->path << "</" << XML_FILE << ">" << endl; } ofs << "</" << XML_ROOT << ">" << endl; ofs.close(); return true; }
通过重载操作符operator[]方法获得子项的数据:
//获取第index个ROOT_FILE子项 //如果index超出当前子项的个数,则返回0 const ROOT_FILE* operator[](size_t index) const { if(index >= files_.size()) { return 0; } return files_[index]; }
xml_handler完整代码展示
#pragma once #include "ACEXML/common/DefaultHandler.h" #include <string> #include <iostream> #include <vector> #include <fstream> using namespace std; //对应xml文件的根节点 typedef struct _ROOT { //版本号 int version; //子项的数量 int count; } ROOT; //对应xml文件的子项 typedef struct _FILE { int id; //文件路径 string path; } ROOT_FILE; #define XML_ROOT "root" #define XML_FILE "file" #define XML_ID "id" #define XML_VER "version" #define XML_COUNT "count" //针对某个具体的xml文件格式进行解析的类 //注意:此对象不提供多进程或多线程按序访问的功能 class xml_handler : public ACEXML_DefaultHandler { public: //构造。 //@param name 字符串,输入,xml文件的路径 xml_handler(char* path): filepath_(path), flag_(0) {} virtual ~xml_handler(){ for(size_t i=0; i<files_.size(); ++i) { if(files_[i]) { delete files_[i]; files_[i] = 0; } } files_.clear(); } //获取xml文件的路径 const string& path() const { return filepath_; } //获取子项的数量,对应root.count const size_t count() const { return files_.size(); } //获取第index个ROOT_FILE子项 //如果index超出当前子项的个数,则返回0 const ROOT_FILE* operator[](size_t index) const { if(index >= files_.size()) { return 0; } return files_[index]; } //获取root信息 const ROOT& root() const { return root_; } //增加一个子项 //@param path 字符串,输入,文件的路径 //@param id 整形,输入,文件id bool increase(const char* path, size_t id) { if(!path || *path == 0 || id == 0) { return false; } ROOT_FILE* file = new(std::nothrow)ROOT_FILE(); //申请内存失败 if(!file) { return false; } file->path = path; file->id = id; files_.push_back(file); return true; } //将当前root、file信息写入到xml文件中 bool flush() const { ofstream ofs(filepath_.c_str(), ios_base::trunc); if(!ofs) { return false; } ofs << "<?xml version="1.0"?>" << endl; ofs << "<" << XML_ROOT << " " << XML_VER << "="" << root_.version << "" " << XML_COUNT << "="" << root_.count << "" >" << endl; for(size_t i=0; i<files_.size(); ++i) { ofs << " <" << XML_FILE << " " << XML_ID << "="" << files_[i]->id << "" >" << files_[i]->path << "</" << XML_FILE << ">" << endl; } ofs << "</" << XML_ROOT << ">" << endl; ofs.close(); return true; } //将当前root、file信息输出到控制台 void dump() const { cout << "<?xml version="1.0"?>" << endl; cout << "<" << XML_ROOT << " " << XML_VER << "="" << root_.version << "" " << XML_COUNT << "="" << root_.count << "" >" << endl; for(size_t i=0; i<files_.size(); ++i) { cout << " <" << XML_FILE << " " << XML_ID << "="" << files_[i]->id << "" >" << files_[i]->path << "</" << XML_FILE << ">" << endl; } cout << "</" << XML_ROOT << ">" << endl; } public: virtual void characters( const ACEXML_Char *ch, size_t start, size_t length ) { if(flag_ == FLAG_FILE) { files_[files_.size() - 1]->path = ch; } flag_ = FLAG_NULL; } virtual void endDocument( void ) { } virtual void endElement( const ACEXML_Char *namespaceURI, const ACEXML_Char *localName, const ACEXML_Char *qName ) { } virtual void endPrefixMapping( const ACEXML_Char *prefix ) { } virtual void ignorableWhitespace( const ACEXML_Char *ch, int start, int length ) { } virtual void processingInstruction( const ACEXML_Char *target, const ACEXML_Char *data ) { } virtual void setDocumentLocator( ACEXML_Locator *locator ) { locator_ = locator; } virtual void skippedEntity( const ACEXML_Char *name ) { } virtual void startDocument( void ) { } virtual void startElement( const ACEXML_Char *namespaceURI, const ACEXML_Char *localName, const ACEXML_Char *qName, ACEXML_Attributes *atts ) { //file节点 if(stricmp(localName, XML_FILE) == 0) { if(root_.count == files_.size()) { return; } flag_ = FLAG_FILE; ROOT_FILE* file = new ROOT_FILE(); files_.push_back(file); for(int i=0; i<atts->getLength(); ++i) { const char* qname = atts->getQName(i); if(stricmp(qname, XML_ID) == 0) { file->id = atoi(atts->getValue(i)); } } } //root节点 else if(stricmp(localName, XML_ROOT) == 0) { flag_ = FLAG_ROOT; for(int i=0; i<atts->getLength(); ++i) { const char* qname = atts->getQName(i); if(stricmp(qname, XML_VER) == 0) { root_.version= atoi(atts->getValue(i)); } else if(stricmp(qname, XML_COUNT) == 0) { root_.count = atoi(atts->getValue(i)); } } } } virtual void startPrefixMapping( const ACEXML_Char *prefix, const ACEXML_Char *uri ) { } virtual void notationDecl( const ACEXML_Char *name, const ACEXML_Char *publicId, const ACEXML_Char *systemId ) { } virtual void unparsedEntityDecl( const ACEXML_Char *name, const ACEXML_Char *publicId, const ACEXML_Char *systemId, const ACEXML_Char *notationName ) { } virtual ACEXML_InputSource * resolveEntity( const ACEXML_Char *publicId, const ACEXML_Char *systemId ) { return 0; } virtual void error( ACEXML_SAXParseException &exception ) { } virtual void fatalError( ACEXML_SAXParseException &exception ) { } virtual void warning( ACEXML_SAXParseException &exception ) { } private: //xml文件路径 string filepath_; ACEXML_Locator* locator_; ROOT root_; vector<ROOT_FILE*> files_; enum FLAG { FLAG_NULL, FLAG_ROOT, FLAG_FILE, }; int flag_; };
xml_parser完整代码展示
#include "ACEXML/common/FileCharStream.h" #include "ACEXML/parser/parser/Parser.h" //对xml文件进行解析的类,将解析后的数据保存的格式依赖于HANDLER类 //HANDLER 必须继承自ACEXML_DefaultHandler类,并且提供"string path();"这样的方法 //注意:此对象不提供多进程或多线程按序访问的功能 template< typename HANDLER> class xml_parser { public: static bool parse(HANDLER& handler) { ACEXML_FileCharStream* fstm = new(std::nothrow)ACEXML_FileCharStream(); if(!fstm) { return false; } string path = handler.path(); if(path.empty()) { return false; } fstm->open(path.c_str()); //ACEXML_InputSource类的析构方法中会通过“delete fstm”来释放内存 ACEXML_InputSource input(fstm); ACEXML_Parser parser; parser.setContentHandler(&handler); parser.setDTDHandler(&handler); parser.setEntityResolver(&handler); parser.setErrorHandler(&handler); try { parser.parse(&input); } catch(const ACEXML_Exception* ex) { ex->print(); return false; } catch(std::bad_alloc& ex) { return false; } return true; } };
调用代码展示
#include "xml_handler.h" #include "xml_parser.h" int main(int argc, char* argv[]) { xml_handler handler("D:\test.xml"); xml_parser<xml_handler>::parse(handler); cout << "root version:" << handler.root().version << ", count:" << handler.root().count << endl; cout << "---------------------" << endl; for(size_t i=0; i<handler.count(); ++i) { cout << "file id:" << handler[i]->id << ", path:" << handler[i]->path << endl; } return 0; };