在C++中,解析XML文件通常需要使用XML的解析類或庫,如DOM(DOCUMENT OBJECT MODEL)或SAX(SIMPLE API FOR XML),來解析XML文件,從而得到所需的數(shù)據(jù)。雖然比較方便,但是這種方法的效率不是很高,而把XML解析為C++的類,不但能提高解析的效率,而且還能提供給用戶面向?qū)ο蟮乃袃?yōu)點(diǎn)。
一. 通常用DOM或SAX解析XML文件所帶來的問題
C++的程序在處理XML文件時(shí)常常會(huì)碰到解析的問題,有時(shí)需要根據(jù)一定的規(guī)則自己寫一個(gè)解析的類或包。因此,使用DOM或SAX來解析XML文件就顯得相對(duì)比較簡(jiǎn)單。但是解析XML文件,一個(gè)非常重要的問題,即對(duì)于一些不同的語言,如JAVA,C++,XML沒有一個(gè)固定的標(biāo)簽(如關(guān)鍵字),因此,開發(fā)一個(gè)適合于多種語言的通用解析器就顯得非常由必要。
為了解決這個(gè)問題,開發(fā)出的這個(gè)解析器要能動(dòng)態(tài)的列出標(biāo)簽和解析XML文件的規(guī)則。即對(duì)于一個(gè)DOM的解析器要確定一個(gè)XML的模式文件,從而可以對(duì)XML文件中的內(nèi)容進(jìn)行有效性的確認(rèn)。
如果一個(gè)應(yīng)用程序需要對(duì)XML文件進(jìn)行不同的讀寫操作,DOM這個(gè)解析模式是非常合適的。因?yàn)樗鼘?duì)于不同的XM文件,它的源代碼都不需要改變。但是,引用程序?qū)ML文件的操作由可能進(jìn)行的比較少,在這種情況下,如果使用DOM解析器解析XML文件,就加重程序的負(fù)擔(dān)。因此,根據(jù)不同的情況來選擇不同的解析器,使非常有必要的。而且有時(shí)還需自己開發(fā)出一個(gè)合適的解析器。
二. 從XML模式(SCHEMAS)中派生出的靜態(tài)類
和在某些情況下可以從XML模式中派生出C++類,對(duì)XML文件進(jìn)行寫操作,提高程
序的效率一樣,也可以從XML模式中派生出C++類,對(duì)XML文件進(jìn)行解析,即讀操作。
在C++中,為了生成某個(gè)語言的解析類,通常需要如下的步驟:首先需要編寫LEX規(guī)則或YACC語法的代碼,然后在根據(jù)特定的XML輸入文件生成LEXER和解析類。如下圖一所示:
但是,這個(gè)過程非常繁瑣,而且對(duì)于每個(gè)不同的XML文件形式,都需要重復(fù)進(jìn)行如下的步上的步驟。一個(gè)更有效的做法是先編寫一個(gè)用作翻譯的程序,把XML模式文件轉(zhuǎn)化為相對(duì)應(yīng)的LEX規(guī)則或YACC語法,如下圖二所示:
下面的程序(程序一)說明了對(duì)于一個(gè)XML的DTD文件ACMEPC.DTD所生成的語法。
acmepcxml::XMLImporter importer;
// Call the acmepcxml::Initialize() function to register the create functions
// for the acmepcxml classes
acmepcxml::Initialize();
try {
importer.ImportFromFile(sInputFileName, fPreprocess);
}
catch (eXactML::XException & e)
{
std::cerr << e.GetMsg() << std::endl;
std::cerr << "in " << e.GetSourceFile() << " at line number " <<
e.GetSourceLine() << std::endl;
return 1;
}
cout << "Read in XML file with no errors." << std::endl;
acmepc *acmepc = dynamic_cast (importer.GetXObject());
cout << "Successfully cast XML importer root to acmepcxml::acmepc" << std::endl;
// validate the data in the classes, throws an exception if bad.
try {
acmepc->IsValid();
}
catch (eXactML::XException & e) {
cout << "Exception in validating XML" << std::endl;
cout << e.GetMsg() << std::endl;
eXactML::XMLImporterBase::DeleteImportedXObject(acmepc);
return 1;
}
cout << "Validated acmepcxml::acmepc object" << std::endl;
// generate the XML
try {
acmepc->EmitXML(cout);
}
catch (eXactML::XException e) {
cout << "Exception in generating XML" << std::endl;
cout << e.GetMsg() << std::endl;
eXactML::XMLImporterBase::DeleteImportedXObject(acmepc);
return 1;
}
cout << "Emitted XML to file worked fine." << std::endl;
eXactML::XMLImporterBase::DeleteImportedXObject(acmepc);
return 0;
程序一
type CDATA #REQUIRED
speed CDATA #REQUIRED>
type (IDE|EIDE|SCSI) "IDE"
size CDATA #REQUIRED
units (GB|TB) "GB">
n CDATA #REQUIRED
units CDATA #REQUIRED>
n CDATA #REQUIRED
units CDATA #REQUIRED>
ACMEPC.DTD文件
在ACMEPCXML_PARSER.Y中生成的YACC輸入和在ACMEPCXML_LEXER.L中生成的LEX輸入。所有的這些類和解析器都包含在C++中的ACMEPCXML的名字空間中。
使用這個(gè)生成的通用解析器非常簡(jiǎn)單,只需生成類ACMEPCXML::XMLIMPORTER的一個(gè)實(shí)例,然后調(diào)用類中的Initiliaze()這個(gè)初始化的成員函數(shù),在把XML文件傳給ImportFromFile()這個(gè)程序函數(shù)。這個(gè)引入的類通過GetXObject()來發(fā)布這個(gè)樹形的根結(jié)點(diǎn)。這個(gè)基本的類在動(dòng)態(tài)的返回給在ACMEPC.DTD文件中定義的XML上下文的ACMPEPC類。
三. 生成的通用解析類的優(yōu)點(diǎn)
同一些其它的標(biāo)準(zhǔn)解析器或類相比,如DOM,SAX,自己生成的通用解析類主要由如下的幾個(gè)特點(diǎn)和優(yōu)點(diǎn)。
1. 最根本也是最重要的─解析的速度快。經(jīng)過測(cè)試,根據(jù)XML模式生成的通用解析器,要比最快的DOM解析器速度要快三倍以上,而且內(nèi)存的占用也很少。這主要是因?yàn)閷?duì)于XML文件的輸入不需要經(jīng)過有效性的檢查和判斷。這個(gè)工作早在由輸入文件生成YACC時(shí),就已經(jīng)做好和強(qiáng)制性的檢查。
2. 生成的通用解析器能和其它的派生類非常好的結(jié)合在一起。而且不用象使用DOM一樣,生成一個(gè)DOM的數(shù)形結(jié)構(gòu),然后在對(duì)這些數(shù)據(jù)進(jìn)行操作。通用解析器能直接的生成XML模式的派生類,這樣就省去了中間的步驟。當(dāng)然,該解析類也能很好的和STL或MFC的類庫結(jié)合在一起
3. 你能得到鏈接到你應(yīng)用程序組件的所用源代碼。只要嚴(yán)格遵循符合GNU規(guī)范的FLEX和BISON工具的使用,生成的代碼就能夠在任何的操作系統(tǒng)上運(yùn)行,從而很好的解決了跨平臺(tái)的問題。
4. 最后也是最令人激動(dòng)的一個(gè)優(yōu)點(diǎn)是使用LEX和YACC可以方便的來操作和處理XML文件中的實(shí)體元素(ENTITY)。你可以在輸入文件中自動(dòng)擴(kuò)展實(shí)體的數(shù)量。XML中的實(shí)體也可以如C程序中的宏定義一樣,是C編譯器提前預(yù)處理。當(dāng)你要處理大量的實(shí)體元素是,將非常有效。
四. 總結(jié)
由XML模式(SCHEMA)生成C++的類,作為解析器來解析XML的輸入文件是件非常令人激動(dòng)的事,它不但能減少重復(fù)的編碼,更能提高你的效率。在不久的將來該項(xiàng)技術(shù)將會(huì)由長(zhǎng)足的發(fā)展。
五. 附該程序的所有源代碼(包括C++的類和DTD文件,XML文件 )
xmlparser.zip