在
大概三年前
從的XML
在
MSXML類庫能在win
作為一個獨立的組件
XML分析模式
既然XML是一種標記語言
所有的XML分析器
總的來說
SAX分析器利用客戶端應用程序通過現存的指定平台的對象的實例去處理分析事件
閱讀器是基於
XmlReader類
XML閱讀器支持一個編程接口
從閱讀器中看XML文檔不是一個標簽文本文件
閱讀器和XMLDOM分析器有幾點不同的地方
XmlReader是一個抽象類
可以用多種方法創建XmlTextReader類的實例
XmlTextReader reader = new XmlTextReader(file);
注意
Figure
string GetXmlFileNodeLayout(string file)
{
// 創建一個XmlTextReader類使它指向目標XML文檔
XmlTextReader reader = new XmlTextReader(file);
// 循環取出節點的文本並放入到StringWriter對象實例中
StringWriter writer = new StringWriter();
string tabPrefix =
while (reader
{
// 寫開始標志
if (reader
{
//根據元素所處節點的深度
tabPrefix = new string(
writer
}
else
{
//寫結束標志
if (reader
{
tabPrefix = new string(
writer
}
}
}
// 輸出到屏幕
string buf = writer
writer
// 關閉流
reader
return buf;
}
圖三演示了一個簡單的用於輸出一個給定的XML文檔的節點元素的函數
每個節點的類型是XmlNodeType枚舉中的一種
<mags>
<mag name=
MSDN Magazine
</mag>
<mag name=
MSDN Voices
</mag>
</mags>
用上面的程序輸出的結果如下
<mags>
<mag>
</mag>
<mag>
</mag>
</mags>
子節點的縮進量是根據閱讀器的深度屬性(Depth屬性)設置的
如前所述
if (reader
while(reader
buf += reader
reader
當你完成對屬性集的處理時
分析屬性值
大部分情況下
假設你有以下的XML數據片斷
讓我們先確認
在某些解決方案中
ReadAttributeValue方法分析屬性值
while(reader
{
if (reader
// Resolve the
// the result to a buffer
buf += YourResolverCode(reader
else
// Just append the value to the buffer
buf += reader
}
當屬性值全部被分析後
處理XML文本(Text)
當我們在處理XML標簽文本時
XmlConvert類提供了把非XML標准的命名轉換成標准的XML命名的功能
XmlConvert
與此相反的方法是DecodeName
XmlConvert
在XML文檔中的空格即重要也不重要
<MyNode XML:space=
<!
???
</MyNode>
在xml中
通過XmlTextReader類的WhiteSpaceHandling屬性你可以處理空格
String和Fragment
程序員把在MSXML的程序剪切下來
XmlTextReader其中一個構造函數接受一個TextReader派生對象和一個XML reader作參數(該閱讀器以text reader的內容為基礎創建)
string xmlText =
StringReader strReader = new StringReader(xmlText);
XmlTextReader reader = new XmlTextReader(strReader);
另外
一個指定類型的XML字符串是一個XML片斷(fragment)
Dino
Esposito
public XmlTextReader(
string xmlFragment
XmlNodeType fragType
XmlParserContext context
);
xmlFragment參數包括了XML字符串分析帶驗證的閱讀器
XMLValidatingReader類實現了XmlReader類
你可以用XmlVlidatingReader類去驗證XML文檔和XML片斷
XmlVlidatingReader類只實現了非常小的XML閱讀器必備的一個功能子集
public XmlValidatingReader(XmlReader);
public XmlValidatingReader(Stream
public XmlValidatingReader(string
帶驗證的XML閱讀器能分析任何的XML片斷
XmlVlidatingReader類中有重大改變的方法非常少(相對其它reader類來說)
帶驗證的XML閱讀器正如其名
Figure
using System;
using System
using System
class MyXmlValidApp
{
public MyXmlValidApp(String fileName)
{
try {
Validate(fileName);
}
catch (Exception e) {
Console
Console
e
}
}
private void Validate(String fileName)
{
XmlTextReader xtr = new XmlTextReader(fileName);
XmlValidatingReader vreader = new XmlValidatingReader(xtr);
vreader
vreader
ValidationEventHandler(this
vreader
vreader
while (vreader
xtr
vreader
}
public void ValidationEventHandle(Object sender
ValidationEventArgs args)
{
Console
}
public static void Main(String[] args)
{
MyXMLValidApp o = new MyXmlValidApp(args[
return;
}
}
ValidationType屬性設置驗證的類型
驗證發生在用戶用Read方法向前移動指針時
驗證對象在內部調用兩個不同風格的對象
如果節點有子節點
注意
節點閱讀器
XML閱讀器提供一種增量式的方法(一個一個節點的讀)來處理文檔的內容
就像XmlTextReader訪問指定XML流中所有節點一樣
// xmldomNode is the XML DOM node
XmlNodeReader nodeReader = new XmlNodeReader(xmldomNode);
while (nodeReader Read())
{
// Do something here
}
當你要在配置文件(例如fig文件)中引用自定義的數據時 先把這些數據填充到XMLDOM樹中 然後用XmlNodeReader類與XMLDOM類結合處理這些數據 這也是高效的
XMLTextWriter類
用在本節中的方法創建XML文檔顯然並不困難
XML Writer類以只前(forward
我們先來看看XML writers和舊的writers的不同點
StringBuilder sb = new StringBuilder( );
sb Append( );
foreach(string s in theArray) {
sb Append(
sb Append(s);
sb Append( \\\\\\\\ /> );
}
sb Append( );
代碼通過循環取出數據中的元素 寫好標簽文本並把它們累加到一個string中 代碼保證輸出的內容是格式良好的並且注意了新行的縮進 及支持命名空間 當創建的文檔結構比較簡單時 這種方法可能不會有錯誤 然而 當你要支持處理指令 命名空間 縮進 格式化以及實體的時候 代碼的數量就成指數級增長 出錯的可能性也隨之增長
XML writer寫方法功能對應每個可能的XML節點類型 它使創建xml文檔的過程更符合邏輯 更少的信賴於繁瑣的標記語言 圖六演示了怎麼樣用XmlTextWriter類的方法來連接一個string數據 代碼很簡潔 用XML writer的代碼更容易讀 結構更好
Figure Serializing a String Array
void CreateXmlFileUsingWriters(String[] theArray string filename)
{
// Open the XML writer (用默認的字符集)
XmlTextWriter xmlw = new XmlTextWriter(filename null);
xmlw Formatting = Formatting Indented;
xmlw WriteStartDocument();
xmlw WriteStartElement( array );
foreach(string s in theArray)
{
xmlw WriteStartElement( element );
xmlw WriteAttributeString( value s);
xmlw WriteEndElement();
}
xmlw WriteEndDocument();
// Close the writer
xmlw Close();
}
然而XML writer並不是魔術師 它不能修復輸入的錯誤 XML writer不會檢查元素名和屬性名是否有效 也不保證被用的任何的Unicode字符集適合當前架構的編碼集 如上所述 為了避免輸出錯誤 必須要杜絕非XML字符 但是writer沒有提供這種方法
另外 當創建一個屬性節點時 Writer不會檢驗屬性節點的名稱是否與已存在的元素節點的名稱相同 最後 XmlWriter類不是一個帶驗證的Writer類 也不保證輸出是否符合schema或者DTD 在 NET Framework中帶驗證的writer類目前來說還沒有提供 但是在我寫的《Applied XML Programming for Microsoft NET (Microsoft Press? )》書中 我自己寫了一個帶驗證的Writer組件 你可以到下面的網址去下載源碼
圖七列出了XML writer的一些狀態值(state) 這些值都源於WriteState枚舉類 當你創建一個Writer 它的初始狀態為Start 表示你將要配置該對象 實際上writer沒有開始 下一個狀態是Prolog 該狀態是當你調用WriteStartDocument方法開始工作的時候設置的 然後 狀態的轉換就取決於你的寫的文檔及文檔的內容了 Prolog狀態一直保留到當你增加一個非元素節點時 例如注釋元素 處理指令及文檔類型 當第一個節點也就是根節點寫完後 狀態就變為Element 當你調用WriterStartAtribute方法時狀態轉換為Attribute 而不是當你調用WriteAtributeString方法寫屬性時轉換為該狀態 如果那樣的話 狀態應該是Element 當你寫一個閉標簽(>)時 狀態會轉換成Content 當你寫完文檔後 調用WriteEndDocument方法 狀態就會返回為Start 直到你開始寫另一個文檔或者把Writer關掉
Figure States for XML Writer
State
Description
Attribute
The writer enters this state when an attribute is being written
Closed
The Close method has been called and the writer is no longer
available for writing operations
Content
The writer enters this state when the content of a node is being written
Element
The writer enters this state when an element start tag is being written
Prolog
The writer is writing the prolog of a well formed XML document
Start
The writer is in an initial state awaiting for a write call to be issued
Writer 把輸出文本存在內部的一個緩沖區內 一般情況下 緩沖區會被刷新或者被清除 當Writer被關閉前XML文本應該要寫出 在任何時你都可以通過調用Flush方法清空緩沖區 把當前的內容寫到流中(通過BaseStream屬性暴露流) 然後釋放部分占用的內存 Writer仍保持為打開狀態(open state) 可以繼續操作 注意 雖然寫了部分的文檔內容 但是在Writer沒有關閉前其它的程序是不能處理該文檔的
可以用兩種方法來寫屬性節點 第一種方法是用WriteStartAtribute方法去創建一個新的屬性節點 更新Writer的狀態 接著用WriteString方法設置屬性值 寫完後 用WriteEndElement方法結束該節點 另外 你也可以用WriteAttributeString方法去創建新的屬性節點 當writerr的狀態為Element時 WriterAttributeString開始工作 它單獨創建一個屬性 同樣的 WriteStartElement方法寫節點的開始標簽(<) 然後你可以隨意的設置節點的屬性和文本內容 元素節點的閉標簽都帶 / > 如果想寫閉標簽可以用WriteFullEndElement方法來寫
應該避免傳送給寫方法的文本中包含敏感的標記字符 例如小於號(<) 用WriteRaw方法寫入流的字符串不會被解析 我們可以用它來對xml文檔寫入特殊的字符串 下面的兩行代碼 第一行輸出的是 < 第二行輸出 < :
writer WriteString( < );
writer WriteRaw( < );
讀寫流
有趣的是
writer WriteBase (
Encoding Unicode GetBytes(buf)
buf Length* );
圖八中代碼演示了把一個string數據轉換為Base 編碼的XML流 圖九是輸出的結果
Figure Persisting a String Array as Base
using System;
using System Text;
using System IO;
using System Xml;
class MyBase Array
{
public static void Main(String[] args)
{
string outputFileName = test xml ;
if (args Length > )
outputFileName = args[ ]; // file name
// 把數組轉換成XML
String[] theArray = { Rome New York Sydney Stockholm
Paris };
CreateOutput(theArray outputFileName);
return;
}
private static void CreateOutput(string[] theArray string filename)
{
// 打開XML writer
XmlTextWriter xmlw = new XmlTextWriter(filename null);
//使子元素根據 Indentation 和 IndentChar 設置縮進 此選項只對元素內容進行縮進
xmlw Formatting = Formatting Indented;
//書寫版本為 的 XML 聲明
xmlw WriteStartDocument();
//寫出包含指定文本的注釋
xmlw WriteComment( Array to Base XML );
//開始寫出array節點
xmlw WriteStartElement( array );
//寫出具有指定的前綴 本地名稱 命名空間 URI 和值的屬性
xmlw WriteAttributeString( xmlns x null dinoe:msdn mag );
// 循環的寫入array的子節點
foreach(string s in theArray)
{
//寫出指定的開始標記並將其與給定的命名空間和前綴關聯起來
xmlw WriteStartElement( x element null);
//把S轉換成byte[]數組 並把byte[]數組編碼為 Base 並寫出結果文本
要寫入的字節數為s總長度的 倍 一個string占的字節數是 字節
xmlw WriteBase (Encoding Unicode GetBytes(s) s Length* );
//關閉子節點
xmlw WriteEndElement();
}
//關閉根節點 只有兩級
xmlw WriteEndDocument();
// 關閉writer
xmlw Close();
// 讀出寫入的內容
XmlTextReader reader = new XmlTextReader(filname);
while(reader Read())
{
//獲取節點名為element的節點
if (reader LocalName == element )
{
byte[] bytes = new byte[ ];
int n = reader ReadBase (bytes );
string buf = Encoding Unicode GetString(bytes);
Console WriteLine(buf Substring( n));
}
}
reader Close();
}
}
Figure String Array in Internet Explorer
Reader類有專門的解釋Base
XmlTextReader reader = new XmlTextReader(filename);
while(reader
if (reader
byte[] bytes = new byte[
int n = reader
string buf = Encoding
Console
}
}
reader
從byte型轉換成string型是通過Encoding類的GetString方法實現的
設計XMLReadWriter類
如前面所說
像一般的只讀操作一樣
一個簡單有效的辦法是從只讀流中拷貝節點對象到write流中
Figure Using the WriteNode Method
XmlTextReader reader = new XmlTextReader(inputFile);
XmlTextWriter writer = new XmlTextWriter(outputFile);
// 配置 reader 和 writer
writer Formatting = Formatting Indented;
reader MoveToContent();
// Write根節點
writer WriteStartElement(reader LocalName);
// Read and output every other node
int i= ;
while(reader Read())
{
if (i % )
writer WriteNode(reader false);
i++;
}
// Close the root
writer WriteEndElement();
// Close reader and writer
writer Close();
reader Close();
我的XmlTextReadWriter類並沒有從XmlReader或者XmlWriter類中繼承 取而代之的是另外兩個類 一個是基於只讀流(stream)的操作類 另一個是基於只寫流的操作類 XmlTextReadWriter類的方法用Reader對象讀數據 寫入到Writer對象 為了適應不同的需求 內部的Reader和Writer 對象分別通過只讀的Reader和Writer屬性公開 圖十一列出了該類的一些方法
Figure XmlTextReadWriter Class Methods
Method
Description
AddAttributeChange
Caches all the information needed to perform a change on
a node attribute All the changes cached through this method are
processed during a successive call to WriteAttributes
Read
Simple wrapper around the internal reader s Read method
WriteAttributes
Specialized version of the writer s WriteAttributes method
writes out all the attributes for the given node
taking into account all the changes cached through
the AddAttributeChange method
WriteEndDocument
Terminates the current document in the writer and closes
both the reader and the writer
WriteStartDocument
Prepares the internal writer to output the document and add
a default comment text and the standard XML prolog
這個新類有一個Read方法 它是對Reader的read方法的一個簡單的封裝 另外 它提供了WriterStartDocument和WriteEndDocument方法 它們分別初始化/釋放(finalize)了內部Reader和writer對象 還處理所有I/O操作 在循環讀節點的同時 我們就可以直接的修改節點 出於性能的原因 要修改屬性必須先用AddAttributeChange方法聲明 對一個節點的屬性所作的所有修改都會存放在一個臨時的表中 最後 通過調用WriteAttribute方法提交修改 清除臨時表
圖十二所示的代碼演示了客戶端用XmlTextReadWriter類在讀操作的同時修改屬性值的優勢 在本期的msdn中提供了XmlTextReadWriter類的C#和VB源代碼下載(見本文開頭提供的鏈接)
Figure Changing Attribute Values
private void ApplyChanges(string nodeName string attribName
string oldVal string newVal)
{
XmlTextReadWriter rw = new XmlTextReadWriter(InputFileName Text
OutputFileName Text);
rw WriteStartDocument(true CommentText Text);
// 手工修改根節點
rw Writer WriteStartElement(rw Reader LocalName);
// 開始修改屬性
// (可以修改更多節點的屬性)
rw AddAttributeChange(nodeName attribName oldVal newVal);
// 循環處理文檔
while(rw Read())
{
switch(rw NodeType)
{
case XmlNodeType Element:
rw Writer WriteStartElement(rw Reader LocalName);
if (nodeName == rw Reader LocalName)
// 修改屬性
rw WriteAttributes(nodeName);
else
// deep copy
rw Writer WriteAttributes(rw Reader false);
if (rw Reader IsEmptyElement)
rw Writer WriteEndElement();
break;
}
}
// Close the root tag
rw Writer WriteEndElement();
// Close the document and any internal resources
rw WriteEndDocument();
}
XmlTextReadWriter類不僅可以讀XML文檔 也可以寫XML文檔 你可以它來讀XML文檔的內容 如果需要 你還可以用它來做一些基本的更新操作 基本的更新操作在這裡是指修改某個已存在的屬性的值或者某個節點的內容 又或者是增加一個新的屬性或節點 對於更復雜的操作 最好還是用XMLDOM分析器
總結
Reader和 Framework中處理XML數據的根本 它們提供了對所有XML數據訪問功能的原始的API Reader像一個新的分析器類 它即有XMLDOM的強大 又有SAX的快速簡單 Writer為簡單的創建XML文檔而設計 雖然Reader和Writer都是 NET Framework中的一小塊 但是它們是相互獨立的API 在本文中 我們只討論了怎麼樣用Reader和Writer完成一些主要的工作 介紹了驗證分析器的原理機制 並把Reader和writer整合在一個單獨的類中 上述所有的這些類都是輕量級的 類似於游標式的XMLDOM分析器
From:http://tw.wingwit.com/Article/program/ASP/201311/21740.html