System.Xml命名空間下,XmlReader和XmlWriter類是用於讀寫xml文檔的抽象類別,它們使用流模型
使用XmlReader類讀XML文檔,它提供對XML資料的快速、非緩衝、只向前、唯讀訪問。
1.XmlReader有3個子類:
1)XmlTextReader:最快的 XmlReader 實現。它檢查 XML 格式是否正確,但不支援驗證。該讀取器不能展開常規實體(dtd中的概念),不支援預設屬性。
XmlReader 在發生 XML 分析錯誤時引發 XmlException 異常。
2)XmlValidatingReader: 可以使用 DTD 或 Schema 驗證資料的 XmlReader 的實現。該讀取器還可以展開常規實體並支援預設屬性。
3)XmlNodeReader: 從 XmlNode 讀取 XML 資料的 XmlReader 的實現
註:讀xml文檔,一般使用XmlTextReader類
===================================================
2.XmlTextReader 類
1)XmlTextReader把xml文檔看作是一個序列化的節點集合,即一個節點流
2)xml文檔中的 元素的開始標記(節點類型為Element)、元素的屬性(Attribute)、元素的常值內容(Text)、標記之間的空白/換行(SignificantWhitespace或Whitespace)、元素的結束標記(EndElement)等 都被視為節點。
3)Read()方法是XmlTextReader類的主要執行個體方法,它執行一次就讀節點流中的一個節點
但Read()方法不會自動讀取屬性節點(Attribute類型的節點),如果當前節點是Element節點後,下一次被讀的節點就是Text(如果當前元素有常值內容)或Whitespace節點(如果當前節點標記預下一個標記之間有空白/換行的話,如果沒有空白換行,就沒有Whitespace節點)
即Read()方法只會讀Element、Text、Whitespace、EndElement等類型的節點
3)如果當前當前節點是Element節點,如果想讀元素的屬性節點的話,可以使用以下方法:
MoveToAttribute 、MoveToFirstAttribute 、MoveToNextAttribute 等
如果當前節點是Attribute節點,要回到屬性所屬元素的節點上的話,可使用MoveToElement 方法
4)隨著當前節點的節點類型的不同,XmlReader類對象的某些屬性也會無效
例如XmlTextReader對象的AttributeCount屬性,對於不是Element、DocumentType 和 XmlDeclaration 類型的節點來說,該屬性無意義
5)關於SignificantWhitespace/Whitespace節點的處理:
XmlTextReader對象的WhitespaceHandling屬性可以指定如何處理空白
3.節點的類型
=====================================================================
節點類型 說明 樣本 XML
=====================================================================
Attribute 屬性 id='123'
---------------------------------------------------------------------
CDATA CDATA節 <![CDATA[my escaped text]]>
---------------------------------------------------------------------
Comment 注釋 <!-- my comment -->
---------------------------------------------------------------------
Document 作為文檔樹的根的文檔對象提供對整個 XML 文檔的訪問
---------------------------------------------------------------------
DocumentFragment 文檔片段
---------------------------------------------------------------------
DocumentType 文件類型聲明 <!DOCTYPE ...>
---------------------------------------------------------------------
Element 元素 <item>
---------------------------------------------------------------------
EndElement 結束元素標記 </item>
---------------------------------------------------------------------
EndEntity 由於調用 ResolveEntity 而使 XmlReader 到達實體替換的末尾時返回
---------------------------------------------------------------------
Entity 實體聲明 <!ENTITY ...>
---------------------------------------------------------------------
EntityReference 對實體的引用 #
---------------------------------------------------------------------
None 如果未調用 Read 方法,則由 XmlReader 返回
---------------------------------------------------------------------
Notation 文件類型聲明中的標記法 <!NOTATION ...>
---------------------------------------------------------------------
ProcessingInstruction 處理指示 <?pi test?>
---------------------------------------------------------------------
SignificantWhitespace 混合內容模型中標記間的空白或 xml:space="preserve"範圍內的空白
---------------------------------------------------------------------
Text 節點的常值內容。可以以Attribute、DocumentFragment、Element、 EntityReference 節點的子節點的形式出現
---------------------------------------------------------------------
Whitespace 標記間的空白
---------------------------------------------------------------------
XmlDeclaration XML 聲明 <?xml version='1.0'?>
=====================================================================
4.XmlTextReader應用樣本:讀一個xml文檔-tmp.xml
文檔tmp.xml的內容:
-----------------------------------------------------------------------
<?xml version="1.0" encoding="GB2312"?>
<bookstore>
<book name="文化苦旅">
<author nation="中國" 年代="當代">餘秋雨&</author>
<price>32</price>
<press>華夏出版社</press>
</book>
</bookstore>
//文檔有一個空白尾行
------------------------------------------------------------------------
C#代碼:
------------------------------------------------------------------------
XmlTextReader xr4 = new XmlTextReader("temp.xml"); //裝載整個文檔到記憶體
xr4.WhitespaceHandling = WhitespaceHandling.All; //設定如何處理空白節點
while(xr4.Read())
{
Console.Write((" Type: " + xr4.NodeType).PadRight(20)); //列印節點類型
Console.Write((" Name: " + xr4.Name).PadRight(18)); //列印節點名稱
Console.WriteLine(" Value: " + xr4.Value); //列印節點的值
if (xr4.HasAttributes) //如果當前節點有屬性的話(非元素節點的此屬性為null)
{
int tmp = xr4.AttributeCount;
for (int i = 0; i < tmp; i++ ) //迴圈列印元素節點的屬性
{
xr4.MoveToAttribute(i); //移動到第n個屬性上
Console.WriteLine((" Type: " + xr4.NodeType).PadRight(20)
+ (" Name: " + xr4.Name).PadRight(16)
+ (" value: " + xr4.Value.PadRight(10))
+ (" AttributeCount: " + xr4.AttributeCount) );
xr4.MoveToElement(); //回到元素節點
}
}
}
xr4.Close(); //關閉流對象
Console.ReadLine();
------------------------------------------------------------------------
輸出:
----------------------------------------------------------------------------------
Type: XmlDeclaration Name: xml Value: version="1.0" encoding="GB2312"
Type: Attribute Name: version value: 1.0 AttributeCount: 2
Type: Attribute Name: encoding value: GB2312 AttributeCount: 2
Type: Whitespace Name: Value:
Type: Element Name: bookstore Value: //該節點沒有屬性,AttributeCount為null
Type: Whitespace Name: Value: //該節點沒有屬性,AttributeCount為null
Type: Element Name: book Value:
Type: Attribute Name: name value: 文化苦旅 AttributeCount: 1
Type: Whitespace Name: Value:
Type: Element Name: author Value:
Type: Attribute Name: nation value: 中國 AttributeCount: 2
Type: Attribute Name: 年代 value: 當代 AttributeCount: 2
Type: Text Name: Value: 餘秋雨&
Type: EndElement Name: author Value:
Type: Whitespace Name: Value:
Type: Element Name: price Value:
Type: Text Name: Value: 32
Type: EndElement Name: price Value:
Type: Whitespace Name: Value:
Type: Element Name: press Value:
Type: Text Name: Value: 華夏出版社
Type: EndElement Name: press Value:
Type: Whitespace Name: Value:
Type: EndElement Name: book Value: //即book元素的結束標記</book>
Type: Whitespace Name: Value:
Type: EndElement Name: bookstore Value: //即結束標記</bookstore>
Type: Whitespace Name: Value: //文檔的空白尾行
----------------------------------------------------------------------------------
5.XmlTextReader類的常用方法
1)在元素節點和屬性節點之間移動
如果當前節點是元素節點,並且該元素擁有屬性的話:
·使用MoveToAttribute()方法可以移動到屬性節點,該方法要求指定屬性名稱或屬性的位置
·使用MoveToFirstAttribute()方法可以移動到第一個屬性上,並返回true
·如果當前節點是元素節點,使用MoveToNextAttribute()方法等效於MoveToFirstAttribute方法。
如果已經移動到了屬性節點上,並且存在下一個屬性,調用該方法就會移動到下一個屬性節點上
否則,讀取器位置不變並返回false
如果讀取器定位在屬性上,使用MoveToElement()方法,則當前節點移動到屬性所屬的元素節點上。
2)跳過內容-兩種方法
·一種方法是調用使用 MoveToContent 方法直接移動到內容節點
MoveToContent()方法檢查當前節點以查看它是否是內容節點。
內容節點被定義為任意 Text、CDATA、Element、EndElement、EntityReference 或 EndEntity 節點。如果當前節點不是前述內容節點的類型之一,則將跳過該節點並跳到下一個內容節點或檔案結尾。
它一直跳,直到找到下一個內容節點或到檔案的結尾才停止。
如果當前節點是一個屬性節點,則此方法將讀取器移回擁有該屬性的元素。
樣本:
-----------------------------------------------------------------------------
if (reader.MoveToContent() == XmlNodeType.Element && reader.Name == "price")
{
_price = reader.ReadString();
}
-----------------------------------------------------------------------------
·另一種方法是直接調用 Skip 方法,該方法從當前節點跳過所有子節點到下一個同級節點
如果當前節點類型為XmlNodeType.Element,則調用skip方法將跳到同級的下一個節點上。
如果當前節點是屬性節點,則調用skip方法將跳到屬性所屬元素節點的下一個同級節點上。
3)讀方法
·Read()方法:如果成功讀取了下一個節點,則為true;如果沒有其他節點可讀取,則為false
第一次建立和初始化讀取器時,沒有可用的資訊。必須調用 Read() 讀取第一個節點。
·ReadStartElement()方法:
檢查當前節點是否為元素(類型為Element的節點)並將讀取器推進到下一個節點
·ReadEndElement()方法:
檢查當前節點是否為結束標記(類型為EndElement的節點)並將讀取器推進到下一個節點
·ReadAttributeValue()方法:
將當前屬性節點的屬性值分解為一個或多個 Text、EntityReference 或 EndEntity 節點。
如果有可返回的節點,則返回true。如果進行初始調用時讀取器不是定位在屬性節點上,或者如果已讀取了所有屬性值,則返回false
如果是空屬性(如 misc=""),則返回true,同時將屬性值分解為 String.Empty 的單個節點
一般,當讀取器移動到一個屬性節點上後,通過迴圈調用ReadAttributeValue方法來分解屬性的值
樣本:讀xml文檔 <book genre='novel' misc='sale-item &h; 1987'></book>
---------------------------------------------------------------------
.......
reader.MoveToAttribute("misc"); //移動到屬性misc上
while (reader.ReadAttributeValue()) //misc屬性值包含實體應用,屬性值被分解
{ //分解完屬性值後,迴圈結束
if (reader.NodeType==XmlNodeType.EntityReference) //遇到實體引用節點
Console.WriteLine("{0} {1}", reader.NodeType, reader.Name);
else
Console.WriteLine("{0} {1}", reader.NodeType, reader.Value);
}
--------------------------------------------------------------------
4)使用字元流進行完整的內容讀取
方法ReadChars、ReadBinHex、ReadBase64用於讀取大型的流。ReadChars方法原樣讀取文本 (US-ASCII),ReadBase64方法解碼Base64編碼的文本,而ReadBinHex方法則解碼binhex編碼的資料。
ReadChars、ReadBinHex和ReadBase64方法只能用在元素上。在其他節點類型上使用這些方法不起作用
這三個方法都返回元素的開始標記和結束標記之間的所有內容,包括所有標記,就像在讀出流一樣。
·ReadChars方法: public int ReadChars(char[] buffer,int index,int count);
將元素的內容讀入字元緩衝區。通過連續調用此方法,可以讀取當前元素的非常大的嵌入文字資料流
此方法設計只用於元素節點。其他節點類型導致ReadChars返回0
此方法返回元素的實際字元內容,返回元素開始標記和結束標記之間的所有內容,包括標記
ReadChars方法忽略格式不正確的XML標記
當ReadChars方法已到達字元流的結尾時,它傳回值0並且將讀取器定位在結束標記之後
·ReadBase64方法: public int ReadBase64(byte[] array,int offset,int len);
和ReadChars方法一樣,可以連續調用此方法以讀取大的嵌入文字資料流。
它對Base64內容進行解碼,並將解碼的二進位位元組(例如內聯Base64編碼的GIF映像)返回到緩衝區中
·ReadBinHex方法: public int ReadBinHex(byte[] array,int offset,int len);
與 ReadChars 一樣,可以連續調用此方法以讀取大的嵌入文字資料流。
它對BinHex內容進行解碼並將解碼的二進位位元組(例如內聯BinHex編碼的GIF映像)返回到緩衝區中
5)讀取字元內容
·ReadElementString方法:讀取簡單文本元素的方法
調用ReadElementString方法時,讀取器將移動到下一個節點並讀取其簡單常值內容,如果該節點不是簡單文本元素,則會報錯。讀取完之後,讀取器將再向下移動一個節點
·ReadString方法:元素或文本節點的內容當做字串讀取
如果讀取器定位在元素或文本節點以外的位置,或者當前上下文中沒有其他常值內容,則返回Null 字元串
·ReadInnerXml方法:將節點的所有內容(包括子項目、常值內容等)當做字串讀取
如果當前讀取器位於開始標記,則該方法返回開始標記與對應的結束標記之間所有的內容
如果當前讀取器位於屬性節點,則該方法返回屬性的值
如果當前節點既非元素,也非屬性,則返回Null 字元串
·ReadOuterXml方法:此方法類似於 ReadInnerXml,但它還返回開始標記和結束標記
如果當前讀取器位於開始標記,則該方法返回 <開始標記>..內容..<結束標記/> 字串
如果當前讀取器位於屬性節點,則該方法返回 屬性名稱="屬性值" 字串
如果當前節點既非元素,也非屬性,則返回Null 字元串
===================================================
2.XmlValidatingReader類
1)XmlValidatingReader類是一個能夠提供 DTD、XDR 和 XSD 驗證的讀取器,能夠提供資料驗證、解析常規實體的能力和對預設屬性的支援,該類也是繼承自 XmlReader 類。
2)XmlValidatingReader類基本上類似於XmlTextReader類,但它增加了ValidationType、Schema和 SchemaType、XmlResolver 等新的屬性
·ValidationType屬性指示驗證的類型,其範圍為:Auto,DTD,Schema,XDR,None
·Schema屬性用於需要多個xdr或xsd參與驗證的情況,Schema屬性實質上是一個XmlSchemaCollection
·SchemaType屬性返回當前節點的類型:XSD內建類型、使用者自訂類型(simpleType/complexType)
·XmlResolver屬性用於解析外部實體(比如DTD中定義的外部實體)
3)使用XmlValidatingReader類進行驗證的最佳操作:
·建立一個XmlTextReader對象tr,將tr對象傳給XmlValidatingReader建構函式產生一個對象trv
·設定XmlValidatingReader類型對象trv的ValidationType屬性(預設值為Auto)
·為事件ValidationEventHandler定義和分配事件處理方法ValidationEvent
trv.ValidationEventHandler += new ValidationEventHandler(this.ValidationEvent)
因為如果驗證出錯誤時,就會引發trv對象的ValidationEventHandler事件,需要對該事件進行處理
·像使用XmlTextReader類對象那樣使用XmlValidatingReader對象trv
4)僅當在調用Read、ReadInnerXml、ReadOuterXml或Skip方法的過程中,且XmlValidatingReader對象trv的ValidationType屬性不是ValidationType.None時,才可能發生ValidationEventHandler驗證事件
如果不提供ValidationEventHandler事件的處理常式,當遇到層級為Warning的驗證錯誤時,仍會繼續讀取資料不引發異常。當遇到第一個層級為Error的驗證錯誤時,XmlValidatingReader對象trv將會引發異常XmlException,之後trv對象也將無法重新啟動。
當根據架構或DTD進行驗證時,如果發生驗證錯誤,也將引發XmlSchemaException異常
如果某個元素報告驗證錯誤,則不驗證該元素其餘的內容模型,但是將驗證其子級。讀取器只報告給定元素的第一個錯誤
ValidationEventHandler事件處理常式可以使用ValidationEventArgs類對象e的Severity屬性來保證根據結構描述驗證了XML執行個體文檔。Severity屬性可以區分驗證錯誤和驗證警告,驗證錯誤(Severity 等於 XmlSeverityType.Error)表示致命錯誤,而驗證警告(Severity 等於 XmlSeverityType.Warning)指示沒有可用的架構資訊。