在.NET Framework中輕鬆處理XML資料(一) (轉)

amyz發表於2007-08-15
在.NET Framework中輕鬆處理XML資料(一) (轉)[@more@]

本文假設你已熟悉和 microsoft-com::office" />

  前言  在.NET Framework中,XmlTextReader和XmlTextWriter類提供了對xml資料的讀和寫操作。在本文中,作者講述了XML閱讀器(Reader)的體系結構及它們怎樣與XML 和SAX 直譯器結合。作者也演示了怎麼樣運用閱讀器分析和驗證XML文件,怎麼樣建立格式良好的XML文件,以及怎麼樣用讀/寫基於Base64和BinHex編碼的大型的XML文件。最後,作者講了怎麼樣實現一個基於流的讀/寫分析器,它把讀寫器都封裝在一個單獨的類裡。

  概三年前,我參加了一個研討會,主題是“沒有XML,就沒有的未來”。XML確實也在一步一步的發展,它已經嵌入到. NET Framework中了。在本文中,我將講解. NET Framework中用於處理XML文件的的角色和它的內部特性,然後我將演示一些常用的功能。

  從MSXML到.net的XML

  在. NET Framework出現之前,你習慣使用MSXML服務----一個基於COM的類庫---寫的XML的。不像. NET Framework中的類,MSXML類庫的部分程式碼比API更深,它完全的嵌在操作的底層。MSXML的確能夠與你的應用程式通訊,但是它不能真正的與外部環境結合。

MSXML類庫能在中被匯入,也能在CLR中運用,但它只能作為一個外部使用。但是基於.NET Framework的應用程式能直接的用XML類與.NET Framework 的其它名稱空間整合使用,並且寫出來的程式碼易於閱讀。

作為一個獨立的元件,MSXML分析器提供了一些高階的特性如非同步分析。這個特性在.NET Framework中的XML類及.NET Framework的其它類都沒有提供,但是,NET Framework中的XML類與其它的類整合可以很輕易的獲得相同的功能,在這個基礎上你可以增加更多的功能。

.NET Framework中的XML類提供了基本的分析、查詢、轉換XML資料的功能。在.NET Framework中,你可以找到支援查詢和T轉換的類,及讀/寫XML文件的類。另外,.NET Framework也包含了其它處理XML的類,例如的序列化(XmlSerializer和the pFormatter類),應用程式(AppSettingsReader類),資料(DataSet類)。在本文中,我只討論實現基本XML I/O操作的類。

XML分析

既然XML是一種標記語言,就應該有一種工具按一定的語法來分析和理解儲存在文件中資訊。這個工具就是XML分析器---一個元件用於讀標記文字並返回指定平臺的物件。

所有的XML分析器,不管它屬於哪個操作平臺,不外乎都分以下的兩類:基於樹或者基於事件的。這兩類通常都是用XMLDOM(the Microsoft XML Document 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分析器的底層支援。XmlReader、XmlWriter兩個抽象類是所有.NET Framework中XML類的基礎類,包括XMLDOM類、驅動類及配置類。所以在.NET Framework中你有兩種可選的方法去處理XML資料。用XmlReader和XmlWriter類直接處理XML資料,或者用XMLDOM模式處理。更多的關於在.NET Framework中讀文件的介紹可以參見MSDN 2002 年八月刊的欄目文章。

XmlReader

  XML閱讀器支援一個程式設計介面,介面用於連線XML文件,“推出”你要的資料。如果你更深入去了解閱讀器,你會發現閱讀器工作原理類似於我們的桌面應用程式從資料庫中取出資料的原理。資料庫服務返回一個遊標物件,它包含所有查詢結果集,並返回指向目標資料集的開始地址的引用。XML閱讀器的客戶端收到一個指向閱讀器例項的引用。該例項提取底層的資料流並把取出的資料呈現為一棵XML樹。閱讀器類提供只讀、向前的遊標,你可以用閱讀器類提供的方法滾動遊標遍歷結果集中的每一條資料。

  從閱讀器中看XML文件不是一個標籤文字,而是一個序列化的節點集合。它是.NET Framework中的一種特殊的遊標模式;在.NET Framework中,你找不到其它的任何一個類似的API函式。

  閱讀器和XMLDOM分析器有幾點不同的地方。XML閱讀器是隻進的,它沒有父、子、祖宗、兄弟節點的概念,而且是隻讀的。在.NET Framework中,讀寫XML文件是分為兩種完全不同的功能,分別由XmlReader和XmlWriter類來完成。要編輯XML文件,你可以用XMLDOM分析器,或者你自己設計一個類來實現這兩種功能。讓我們開始分析閱讀器的程式功能。

  XmlReader是一個抽象類,你可以繼承並擴充套件它的功能。程式一般都基於下面的三種類:XmlTextReader、XmlValidatingReader或者 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中所有的閱讀器類一樣(如DataReader類),一旦閱讀器物件連線並開啟,你就可以用Read方法去訪問資料了。開始的時候只能用Read方法把指標移到第一個元素;然後我們可以用Read方法或其它方法(如Skip, MoveToContent和ReadInnerXml)移動指標到下一個節點元素。要處理整個XML文件的內容,可以根據Read方法的返回值用一個迴圈遍歷文件內容,因為Read方法返回一個布林值,當讀到文件的尾節點時,Read方法返回false,否則它返回true。

 

Figure 3 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 (reader.Read())


  {


  // 寫開始標誌,如果節點型別為元素


  if (reader.NodeType == XmlNodeType.Element)


  {


  //根據元素所處節點的深度,加入reader.Depth個tab符,然後把元素名寫入到<>中。


    tabPrefix = new string(, reader.Depth);


  writer.WriteLine("{0}", tabPrefix, reader.Name);


  }


  else


  {


  //寫結束標誌,如果節點型別為元素


  if (reader.NodeType == XmlNodeType.EndElement)


  {


  tabPrefix = new string(, reader.Depth);


  writer.WriteLine("{0}{1}>", tabPrefix, reader.Name);


  }


  }


  }


 


  // 輸出到螢幕


  string buf = writer.ToString();


  writer.Close();


 


  // 關閉流


  reader.Close();


 


  return buf;


}


 

  圖三演示了一個簡單的用於輸出一個給定的XML文件的節點元素的函式。該函式先開啟一個XML文件,然後用迴圈處理XML文件中所有的內容。每次Read方法,閱讀器的指標都會向下移一個節點。大部分情況下,用Read方法可以處理的元素節點,但有時候,當你從一個節點移動到下一個節點時,可能是在兩個不同型別的節點間移動。但是Read方法不能在屬性節點之間移動。閱讀器的MoveToContent方法可以讓指標從頭部節點位置跳到第一個內容節點位置。在ProcessingInstruction, DocumentType, Comment, Whitespace和SignificantWhitespace型別節點中也可以用Skip方法移動指標。
  每個節點的型別是XmlNodeType列舉值中的一種,在如圖三所示的程式碼中,我們只用了其中的兩種型別:Element 和 EndElement。輸出原始碼重新定製了原始的文件結構,它丟棄或者說是忽略了XML元素的屬性和節點內容,只輸出了元素節點名。假設我們運用了下面的XML片斷:


 


  MSDN Magazine


 


 


  MSDN Voices


 



用上面的程式輸出的結果如下:


 


 


 


 



子節點的縮排量是根據閱讀器的深度屬性(Depth屬性)設定的,Depth屬性返回一個整形的資料,它表示當前節點的巢狀層次。所有文字都放在StringWriter物件中(一個非常方便的基於流的封裝了StrigBuilder類的類)。


如前所述,閱讀器不會自動透過Read方法訪問屬性節點。要訪問當前元素的屬性節點集合,必須用一個簡單的用MoveToNextAttribute方法的返回值控制的迴圈去遍歷該集合。下面的程式碼用於訪問當前節點的所有屬性,並把屬性的名稱和它的值用逗號分開組合成一個字串:


if (reader.HasAttributes)


while(reader.MoveToNextAttribute())


  buf += reader.Name + "="" + reader.Value + "",";


reader.MoveToElement();

當你完成對屬性集的處理時,呼叫MoveToElement方法使指標返回到屬性所屬的元素節點。準確的說,MoveToElement方法並不是真正的移動指標,因為在處理屬性集時指標從來就沒有從元素節點中移開。MoveToElement方法只不過指向某個內部成員,並依次取得成員的值。例如,用Name屬性獲得某個屬性的屬性名,然後呼叫MoveToElement方法把指標移到其所屬的元素節點處。但是當你不需要繼續處理別的節點時,就不必再呼叫MoveToElement方法了。 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752019/viewspace-957842/,如需轉載,請註明出處,否則將追究法律責任。

相關文章