高效交換XML文檔XML文檔因為其固有的描述性特性而趨向于變得很羅嗦。其結果是文檔會(huì )由于被描述的數據增多而變得很長(cháng),而這種很大的文檔會(huì )在需要同其他實(shí)體進(jìn)行交換時(shí)出現問(wèn)題。和其他文檔(比如普通文本文件(flat file)或者Electronic Data Interchange (EDI))比起來(lái)XML文檔就顯得特別冗長(cháng)。為了舉例說(shuō)明這個(gè)概念,讓我們看看以下這個(gè)普通文本文件: John,Doe,1587,4/18/2000,1234
Anywhere St.,SomeCity,AZ,85222
再看這個(gè)XML文檔:<customers>
<customer customerID="1587">
<firstName>John</firstName>
<lastName>Doe</lastName>
<customerSince>4/18/2000
</customerSince>
<street>1234 Anywhere St.
</street>
<city>SomeCity</city>
<state>AZ</state>
<postalCode>85222</postalCode>
</customer>
</customers>
如果你曾處理過(guò)很多XML文檔,那么你就不會(huì )奇怪于即使這個(gè)XML文檔和這個(gè)以逗號分隔開(kāi)的普通文本文件中包含了相同的原始數據(raw data),XML文檔也顯得比普通文本文件大很多了。畢竟,XML是一種元數據語(yǔ)言(metadata language)(它包含了許多優(yōu)點(diǎn)比如支持解析、驗證、轉換等等),因此決定了其大小會(huì )比另一些同類(lèi)文檔格式大很多。由于XML被更廣泛地作為一種數據交換的方法來(lái)使用,那么被交換的文檔的大小會(huì )降低應用程序的性能和可升級性就是毫無(wú)疑問(wèn)的了。
有很多方法來(lái)使XML文檔的大小最小化,比如(在適當的地方)將元素轉換為屬性,縮寫(xiě)元素和屬性名,去掉不重要的空白處,只定義一些內容。然而無(wú)論你做出何種改變,最終大量的原始數據還是會(huì )形成一個(gè)很大的XML文檔。如果你的XML文檔中包含有很多兆字節,你又該如何在你的企業(yè)中對它們進(jìn)行有效地傳遞或將它們傳遞到其他企業(yè)中去呢?
一種方法是將一個(gè)大的XML文檔分成多個(gè)文檔,它們會(huì )(如果可以切分的話(huà))運行的很好,但這樣還會(huì )產(chǎn)生一些額外的復雜性和確保所有文檔都能被準確發(fā)送和接收的問(wèn)題。即使是被分開(kāi)的小文檔也可能會(huì )由于大量被傳遞的數據而形成幾兆字節大小的文檔。既然存在這些潛在的問(wèn)題,那我們這些XML開(kāi)發(fā)人員該如何更有效地對XML數據進(jìn)行交換呢?(我贊成去打高爾夫。)
你可以用壓縮技術(shù)來(lái)加速各點(diǎn)之間的文檔交換。由于XML是一個(gè)簡(jiǎn)單的文本形式,因此大的文檔可以被壓縮成較小形式。這里顯示的范例程序證明了如何通過(guò)將一個(gè)開(kāi)發(fā)式代碼的.Net組件添加到一個(gè)ZIP存檔文件中來(lái)實(shí)現用程序來(lái)壓縮XML文檔。這么做能夠將文件的大小減至最小并提高數據交換的效率。
盡管.Net的J#語(yǔ)言本身支持壓縮,但構建到.Net框架中的基類(lèi)庫卻不支持。然而,有一個(gè)完全由管制代碼寫(xiě)成的名為SharpZipLib的組件可以被用于壓縮各種類(lèi)型的文檔(在www.icsharpcode.net/OpenSource/SharpZipLib/default.asp中下載該組件)。SharpZipLib是一個(gè)用C#寫(xiě)的、用在.Net中支持Zip、GZip、Tar和BZip2的類(lèi)庫。它是作為一個(gè)assembly來(lái)實(shí)現的,而且它還能夠同任何使用.Net語(yǔ)言的項目結合使用。
我曾在幾個(gè)應用程序中使用過(guò)SharpLibZip的早期beta發(fā)行版,我認為它在對文檔進(jìn)行壓縮和解壓縮方面非常有效。讓我們來(lái)看看如何使用SharpZipLib組件來(lái)實(shí)現用程序壓縮XML文檔。
壓縮XML文檔
盡管SharpZipLib能夠執行好幾種類(lèi)型的壓縮,但我還是決定在范例程序中使用應用最為廣泛的ZIP壓縮格式,因為它很有名,也很好用。為了使代碼能夠被重用,我寫(xiě)了一個(gè)名為Zipper的自定義類(lèi)。Zipper中有一個(gè)名為GenerateZipFile()的靜態(tài)方法(它可以接受指定要保存ZIP文件的路徑)以及一個(gè)包括所有要壓縮的文件路徑集合的ArrayList(見(jiàn)列表1)。
Zipper類(lèi)是SharpZipLib中名為ZipOutputStream類(lèi)的一個(gè)封裝類(lèi)。你幾乎不用寫(xiě)什么代碼,也不用花什么力氣就可以用Zipper來(lái)將多個(gè)文件壓縮到一個(gè)簡(jiǎn)單的ZIP存檔文件中(一個(gè)帶有ZIP擴展名的文件)。這個(gè)GenerateZipFile()方法是通過(guò)建立一個(gè)ZipOutputStream類(lèi)的實(shí)例并通過(guò)其SetLevel屬性設置壓縮級別來(lái)實(shí)現壓縮的。最高壓縮級別可以被設置到9,而最低則為0。
設置好壓縮級別之后,由ArrayList(被傳入GenerateZipFile())方法)所指定的文件內容就會(huì )被處理。一個(gè)生成的計數器(enumerator)會(huì )逐個(gè)列舉該列表中的文件。每個(gè)文件被加載到一個(gè)接受文件名和登錄時(shí)間的ZipEntry對象中。然后ZipEntry對象通過(guò)PutNextEntry()方法被添加到ZipOutputStream對象中。
圖1. 測試Zipper類(lèi)
在文件名被添加到這個(gè)ZIP存檔文件之后將通過(guò)一個(gè)FileStream對象來(lái)讀取其內容。FileStream(位于System.IO命名空間下)用于將文件以字節形式讀入到緩沖區中。你可以通過(guò)調用FileStream對象中的Read()方法來(lái)完成讀取操作。在緩沖區中的字節通過(guò)Write()方法被寫(xiě)入ZipOutputStream對象中。注意Write()方法接受要寫(xiě)入數據流中字節的長(cháng)度以及在緩沖區中的起始位置。該過(guò)程適用于所有包含在傳給GenerateZipFile()方法的ArrayList參數之中的每一個(gè)文件。 當所有條目被添加到這個(gè)ZIP文件之后,它會(huì )以一個(gè)ZIP作為文件擴展名被保存到硬盤(pán)中。
列表2中顯示了一個(gè)用于測試Zipper類(lèi)的簡(jiǎn)單ASP.Net應用程序的代碼(見(jiàn)圖1)。它是從定義一個(gè)要被壓縮的XML文檔路徑和存儲ZIP文件的路徑開(kāi)始的。盡管在這個(gè)例子中只有一個(gè)被壓縮的XML文檔,但是其他文檔的路徑可以被添加到ArrayList對象中來(lái)進(jìn)行壓縮。在所有文件路徑被定義好之后,將會(huì )調用靜態(tài)方法GenerateZipFile()。一旦這個(gè)ZIP文件被建好之后,會(huì )通過(guò)System.Web.Mail命名空間下的類(lèi)來(lái)給最終用戶(hù)發(fā)送一封e-mail。
解壓XML文檔
對XML文檔進(jìn)行壓縮的能力在不同的情況下是非常有用的,但不可避免地會(huì )出現這種情況:有人給你發(fā)送了一個(gè)在解析前需要被展開(kāi)的(extracted)的壓縮文檔。這個(gè)問(wèn)題可以直接通過(guò)使用SharpZipLib中的一個(gè)名為ZipFile的類(lèi)來(lái)解決。在列表3中你可以看到在這個(gè)用于將壓縮文件展開(kāi)到一個(gè)指定目錄下的Zipper類(lèi)中有一個(gè)名為ExtractZipFile()的靜態(tài)方法。代碼首先通過(guò)將一個(gè)FileStream對象(通過(guò)調用File.Open()方法得到的)傳入ZipFile類(lèi)的構造器中來(lái)建立一個(gè)ZipFile實(shí)例。建立好對象之后,ZIP文件中的每個(gè)ZipEntry會(huì )被列舉(enumerate)出來(lái)。然后調用ZipFile對象的GetInputStream()方法,該方法接受一個(gè)要被展開(kāi)的ZipEntry作為參數。從GetInputStream()返回的數據流被讀取到一個(gè)緩沖區中,該緩沖區通過(guò)一個(gè)FileStream被寫(xiě)入到文件里。在調用GetInputStream()時(shí),該ZipFile類(lèi)會(huì )自動(dòng)對ZipEntry進(jìn)行解壓。
在調用ExtractZipFile()方法之后,所有位于ZIP文件中的被壓縮文件會(huì )被展開(kāi)并存儲到硬盤(pán)上。另外,解壓的字節流會(huì )被寫(xiě)入一個(gè)MemoryStream對象中,這在文件被解析前無(wú)需被保存到硬盤(pán)上時(shí)非常有用。
盡管XML是一個(gè)很冗長(cháng)的元數據語(yǔ)言,但大的文檔可以在使用.Net組件(比如SharpLibZip)后被壓縮成一個(gè)很小的文檔。通過(guò)對這些文檔進(jìn)行壓縮,可以縮短不同實(shí)體間文檔交換的時(shí)間,其結果是能夠更快地處理數據。想要試試這個(gè)很好的壓縮/解壓代碼的例子,你可以訪(fǎng)問(wèn)www.xmlforasp.net/codeSection.aspx?csID=95。
關(guān)于作者:
Dan Wahlin(是ASP.NET方面的Microsoft MVP)是Wahlin Consulting LLC公司的總裁并創(chuàng )辦了XML for ASP.NET Developers網(wǎng)站(www.XMLforASP.NET),其中主要研究如何在Microsoft的.Net平臺下使用XML和Web services。他還是一名合作培訓師和演講者,并在美國各地教授“public and on-site XML and .Net”培訓課程。Dan是Professional Windows DNA (Wrox)、ASP.NET Tips、Tutorials 和Code (Sams)等書(shū)的合著(zhù)者,并著(zhù)有XML for ASP.NET Developers (Sams)一書(shū)。他的聯(lián)系方式是dwahlin@xmlforasp.net。
本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請
點(diǎn)擊舉報。