在net Framework中XMLTextReader和XmlTextWriter類提供了對xml數據的讀和寫操作在本文中作者講述了XML閱讀器(Reader)的體系結構及它們怎樣與XMLDOM 和SAX 解釋器結合作者也演示了怎麼樣運用閱讀器分析和驗證XML文檔怎麼樣創建格式良好的XML文檔以及怎麼樣用函數讀/寫基於Base和BinHex編碼的大型的XML文檔最後作者講了怎麼樣實現一個基於流的讀/寫分析器它把讀寫器都封裝在一個單獨的類裡
大概三年前我參加了一個軟件研討會主題是沒有XML就沒有編程的未來XML確實也在一步一步的發展它已經嵌入到 NET Framework中了在本文中我將講解 NET Framework中用於處理XML文檔的API的角色和它的內部特性然後我將演示一些常用的功能
從MSXML到net的XML在 NET Framework出現之前你習慣使用MSXML服務——一個基於COM的類庫——寫Windows的XML的驅動程序不像 NET Framework中的類MSXML類庫的部分代碼比API更深它完全的嵌在操作系統的底層MSXML的確能夠與你的應用程序通信但是它不能真正的與外部環境結合
MSXML類庫能在win中被導入也能在CLR中運用但它只能作為一個外部服務器組件使用但是基於NET Framework的應用程序能直接的用XML類與NET Framework 的其它命名空間整合使用並且寫出來的代碼易於閱讀
作為一個獨立的組件MSXML分析器提供了一些高級的特性如異步分析這個特性在NET Framework中的XML類及NET Framework的其它類都沒有提供但是NET Framework中的XML類與其它的類整合可以很輕易的獲得相同的功能在這個基礎上你可以增加更多的功能
NET Framework中的XML類提供了基本的分析查詢轉換XML數據的功能在NET Framework中你可以找到支持Xpath查詢和XSLT轉換的類及讀/寫XML文檔的類另外NET Framework也包含了其它處理XML的類例如對象的序列化(XmlSerializer和the SoapFormatter類)應用程序配置(AppSettingsReader類)數據存儲(DataSet類)在本文中我只討論實現基本XML I/O操作的類
XML分析模式既然XML是一種標記語言就應該有一種工具按一定的語法來分析和理解存儲在文檔中信息這個工具就是XML分析器——一個組件用於讀標記文本並返回指定平台的對象
所有的XML分析器不管它屬於哪個操作平台不外乎都分以下的兩類基於樹或者基於事件的處理器這兩類通常都是用XMLDOM(the Microsoft XML Document Object Model)和SAX(Simple API for XML)來實現XMLDOM分析器是一個普通的基於樹的API——它把XML文檔當成一個內存結構樹呈現SAX分析器是基於事件的API——它處理每個在XML數據流中的元素(它把XML數據放進流中再進行處理)通常DOM能被一個SAX流載入並執行因此這兩類的處理不是相互排斥的
總的來說SAX分析器與XMLDOM分析器正好相反它們的分析模式存在著極大的差別XMLDOM被很好的定義在它的functionalition集合裡面你不能擴展它當它在處理一個大型的文檔時它要占用很大內存空間來處理functionalition這個巨大的集合
SAX分析器利用客戶端應用程序通過現存的指定平台的對象的實例去處理分析事件SAX分析器控制整個處理過程把數據推出到處理程序該處理程序依次接受或拒絕處理數據這種模式的優點是只需很少的內存空間
NET Framework完全支持XMLDOM模式但它不支持SAX模式為什麼呢?因為NET Framework支持兩種不同的分析模式XMLDOM分析器和XML閱讀器它顯然不支持SAX分析器但這並不意味它沒有提供類似SAX分析器的功能通過XML閱讀器SAX的所有的功能都能很容易的實現及更有效的運用不像SAX分析器NET Framework的閱讀器整個都運作在客戶端應用程序下面這樣應用程序本身就可以只把真正需要的數據推出然後從XML數據流中跳出來而SAX分析模式要處理所有的對應用程序有用和無用的信息
閱讀器是基於NET Framework流模式工作的它的工作方式類似於數據庫的游標有趣的是實現類似游標分析模式的類提供對NET Framework中的XMLDOM分析器的底層支持XmlReaderXmlWriter兩個抽象類是所有NET Framework中XML類的基礎類包括XMLDOM類ADONET驅動類及配置類所以在NET Framework中你有兩種可選的方法去處理XML數據用XmlReader和XmlWriter類直接處理XML數據或者用XMLDOM模式處理更多的關於在NET Framework中讀文檔的介紹可以參見MSDN 年八月刊的Cutting Edge欄目文章
XmlReader類XML閱讀器支持一個編程接口接口用於連接XML文檔推出你要的數據如果你更深入去了解閱讀器你會發現閱讀器工作原理類似於我們的桌面應用程序從數據庫中取出數據的原理數據庫服務返回一個游標對象它包含所有查詢結果集並返回指向目標數據集的開始地址的引用XML閱讀器的客戶端收到一個指向閱讀器實例的引用該實例提取底層的數據流並把取出的數據呈現為一棵XML樹閱讀器類提供只讀向前的游標你可以用閱讀器類提供的方法滾動游標遍歷結果集中的每一條數據
從閱讀器中看XML文檔不是一個標簽文本文件而是一個序列化的節點集合它是NET Framework中的一種特殊的游標模式在NET Framework中你找不到其它的任何一個類似的API函數
閱讀器和XMLDOM分析器有幾點不同的地方XML閱讀器是只進的它沒有父子祖宗兄弟節點的概念而且是只讀的在NET Framework中讀寫XML文檔是分為兩種完全不同的功能分別由XmlReader和XmlWriter類來完成要編輯XML文檔你可以用XMLDOM分析器或者你自己設計一個類來實現這兩種功能讓我們開始分析閱讀器的程序功能
XmlReader是一個抽象類你可以繼承並擴展它的功能用戶程序一般都基於下面的三種類XmlTextReaderXmlValidatingReader或者 XmlNodeReader類所有的這些類都有如圖一的屬性和圖二的方法要注意的是某些屬性的值實際上依賴於實際的某個閱讀器類不同的類與基類可能不同因此在圖一中每個屬性的說明都是以基類為准的例如CanResolveEntity屬性在XmlValidatingReader類中只返回true而在其它的閱讀器類中它卻可以設為false同樣的在圖二中的某些方法的實際返回值對不同的類可能不同例如如果節點類型不是元素節點(element node)所有包含Atrributes的方法的返回值類型都是void XmlTextReader類用只進只讀的方式快速訪問XML數據流閱讀器先驗證XML文檔是否是格式良好的如果不是則拋出一個異常XmlTextReader 檢查 DTD 的格式是否良好但不使用 DTD 對文檔進行驗證XmlTextReader通過XML文檔的文件名或它的URL或者從文件流中載入XML文檔然後快速的處理XML文檔數據如果你需要對文檔的數據進行驗證你可以用XmlValidatingReader類
可以用多種方法創建XmlTextReader類的實例從硬盤中加載文件或從URL地址中加載流(streams)中加載還有就是從文本中讀入XML文檔數據XmlTextReader reader = new XmlTextReader(file)注意所有XmlTextReader類的公共(public)構造函數都要求你指定數據源數據源可以是stream文件或者其它XmlTextReader默認的構造函數是受保護的(protected)所以不能直接使用像NET Framework中所有的閱讀器類一樣(如SqlDataReader類)一旦閱讀器對象連接並打開你就可以用Read方法去訪問數據了開始的時候只能用Read方法把指針移到第一個元素然後我們可以用Read方法或其它方法(如Skip MoveToContent和ReadInnerXml)移動指針到下一個節點元素要處理整個XML文檔的內容可以根據Read方法的返回值用一個循環遍歷文檔內容因為Read方法返回一個布爾值當讀到文檔的尾節點時Read方法返回false否則它返回true
Figure Outputting an XML Document Node Layout
string GetXmlFileNodeLayout(string file)
{
// 創建一個XmlTextReader類使它指向目標XML文檔
XmlTextReader reader = new XmlTextReader(file);
// 循環取出節點的文本並放入到StringWriter對象實例中
StringWriter writer = new StringWriter();
string tabPrefix = ;
while (readerRead())
{
// 寫開始標志如果節點類型為元素
if (readerNodeType == XmlNodeTypeElement)
{
//根據元素所處節點的深度加入readerDepth個tab符然後把元素名寫入到<>中
tabPrefix = new string(\t readerDepth);
writerWriteLine({}<{}> tabPrefix readerName);
}
else
{
//寫結束標志如果節點類型為元素
if (readerNodeType == XmlNodeTypeEndElement)
{
tabPrefix = new string(\t readerDepth);
writerWriteLine({} tabPrefix readerName);
}
}
}
// 輸出到屏幕
string buf = writerToString();
writerClose();
// 關閉流
readerClose();
return buf;
}
圖三演示了一個簡單的用於輸出一個給定的XML文檔的節點元素的函數該函數先打開一個XML文檔然後用循環處理XML文檔中所有的內容每次調用Read方法閱讀器的指針都會向下移一個節點大部分情況下用Read方法可以處理的元素節點但有時候當你從一個節點移動到下一個節點時可能是在兩個不同類型的節點間移動但是Read方法不能在屬性節點之間移動閱讀器的MoveToContent方法可以讓指針從頭部節點位置跳到第一個內容節點位置在ProcessingInstruction DocumentType Comment Whitespace和SignificantWhitespace類型節點中也可以用Skip方法移動指針
每個節點的類型是XmlNodeType枚舉中的一種在如圖三所示的代碼中我們只用了其中的兩種類型Element 和 EndElement輸出源碼重新定制了原始的文檔結構它丟棄或者說是忽略了XML元素的屬性和節點內容只輸出了元素節點名假設我們運用了下面的XML片斷
<mags>
<mag name=MSDN Magazine>
MSDN Magazine
</mag>
<mag name=MSDN Voices>
MSDN Voices
</mag>
</mags>
用上面的程序輸出的結果如下:
<mags>
<mag>
</mag>
<mag>
</mag>
</mags>
子節點的縮進量是根據閱讀器的深度屬性(Depth屬性)設置的Depth屬性返回一個整形的數據它表示當前節點的嵌套層次所有文本都放在StringWriter對象中(一個非常方便的基於流的封裝了StrigBuilder類的類)
如前所述閱讀器不會自動通過Read方法訪問屬性節點要訪問當前元素的屬性節點集合必須用一個簡單的用MoveToNextAttribute方法的返回值控制的循環去遍歷該集合下面的代碼用於訪問當前節點的所有屬性並把屬性的名稱和它的值用逗號分開組合成一個字符串
if (readerHasAttributes)
while(readerMoveToNextAttribute())
buf += readerName + =\ + readerValue + \;
readerMoveToElement();
當你完成對屬性集的處理時調用MoveToElement方法使指針返回到屬性所屬的元素節點准確的說MoveToElement方法並不是真正的移動指針因為在處理屬性集時指針從來就沒有從元素節點中移開MoveToElement方法只不過指向某個內部成員並依次取得成員的值例如用Name屬性獲得某個屬性的屬性名然後調用MoveToElement方法把指針移到其所屬的元素節點處但是當你不需要繼續處理別的節點時就不必再調用MoveToElement方法了
From:http://tw.wingwit.com/Article/program/net/201311/11861.html