.NET Framework簡單處理XML數(shù)據(jù)(4)
發(fā)表時間:2024-06-05 來源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]XmlTextWriter類 用在本節(jié)中的方法創(chuàng)建XML文檔顯然并不困難。多年以來,開發(fā)者都是通過在緩存在連接一些字符串,連接好以后再把緩存中字符串輸出到文件的方式來創(chuàng)建XML文檔。但是以這種方式創(chuàng)建XML文檔的方法只有在你保證字符串中不存在任何細(xì)小的錯誤的時候才有效。.NET Framewo...
XmlTextWriter類
用在本節(jié)中的方法創(chuàng)建XML文檔顯然并不困難。多年以來,開發(fā)者都是通過在緩存在連接一些字符串,連接好以后再把緩存中字符串輸出到文件的方式來創(chuàng)建XML文檔。但是以這種方式創(chuàng)建XML文檔的方法只有在你保證字符串中不存在任何細(xì)小的錯誤的時候才有效。.NET Framework通過用XMLwriter提供了更好的創(chuàng)建XML文檔的方法。
XML Writer類以只前(forward-only)的方式輸出XML數(shù)據(jù)到流或者文件中。更重要的是,XML Writer在設(shè)計時就保證所有的XML數(shù)據(jù)都符合W3C XML 1.0推薦規(guī)范,你甚至不用擔(dān)心忘記寫閉標(biāo)簽,因為XML Writer會幫你寫。XmlWriter是所有 XML writer的抽象基類。.NET Framework只提供唯一的一個writer 類----XmlTextWriter類。
我們先來看看XML writers和舊的writers的不同點,下面的代碼保存了一個string型的數(shù)組:
StringBuilder sb = new StringBuilder("");
sb.Append("");
foreach(string s in theArray) {
sb.Append("
sb.Append(s);
sb.Append("\\\\\\\\"/>");
}
sb.Append("");
代碼通過循環(huán)取出數(shù)據(jù)中的元素,寫好標(biāo)簽文本并把它們累加到一個string中。代碼保證輸出的內(nèi)容是格式良好的并且注意了新行的縮進(jìn),及支持命名空間。當(dāng)創(chuàng)建的文檔結(jié)構(gòu)比較簡單時,這種方法可能不會有錯誤。然而,當(dāng)你要支持處理指令,命名空間,縮進(jìn),格式化以及實體的時候,代碼的數(shù)量就成指數(shù)級增長,出錯的可能性也隨之增長。
XML writer寫方法功能對應(yīng)每個可能的XML節(jié)點類型,它使創(chuàng)建xml文檔的過程更符合邏輯、更少的信賴于繁瑣的標(biāo)記語言。圖六演示了怎么樣用XmlTextWriter類的方法來連接一個string數(shù)據(jù)。代碼很簡潔,用XML writer的代碼更容易讀、結(jié)構(gòu)更好。
Figure 6 Serializing a String Array
void CreateXmlFileUsingWriters(String[] theArray, string filename)
{
// Open the XML writer (用默認(rèn)的字符集)
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并不是魔術(shù)師----它不能修復(fù)輸入的錯誤。XML writer不會檢查元素名和屬性名是否有效,也不保證被用的任何的Unicode字符集適合當(dāng)前架構(gòu)的編碼集。如上所述,為了避免輸出錯誤,必須要杜絕非XML字符。但是writer沒有提供這種方法。
另外,當(dāng)創(chuàng)建一個屬性節(jié)點時,Writer不會檢驗屬性節(jié)點的名稱是否與已存在的元素節(jié)點的名稱相同。最后,XmlWriter類不是一個帶驗證的Writer類,也不保證輸出是否符合schema或者DTD。在.NET Framework中帶驗證的writer類目前來說還沒有提供。但是在我寫的《Applied XML Programming for Microsoft .NET (Microsoft Press?, 2002)》書中,我自己寫了一個帶驗證的Writer組件。你可以到下面的網(wǎng)址去下載源碼:http://www.microsoft.com/MSPress/books/6235.asp.
圖七列出了XML writer的一些狀態(tài)值(state)。這些值都源于WriteState枚舉類。當(dāng)你創(chuàng)建一個Writer,它的初始狀態(tài)為Start,表示你將要配置該對象,實際上writer沒有開始。下一個狀態(tài)是Prolog,該狀態(tài)是當(dāng)你調(diào)用WriteStartDocument方法開始工作的時候設(shè)置的。然后,狀態(tài)的轉(zhuǎn)換就取決于你的寫的文檔及文檔的內(nèi)容了。Prolog狀態(tài)一直保留到當(dāng)你增加一個非元素節(jié)點時,例如注釋元素,處理指令及文檔類型。當(dāng)?shù)谝粋節(jié)點也就是根節(jié)點寫完后,狀態(tài)就變?yōu)镋lement。當(dāng)你調(diào)用WriterStartAtribute方法時狀態(tài)轉(zhuǎn)換為Attribute,而不是當(dāng)你調(diào)用WriteAtributeString方法寫屬性時轉(zhuǎn)換為該狀態(tài)。如果那樣的話,狀態(tài)應(yīng)該是Element。當(dāng)你寫一個閉標(biāo)簽(>)時,狀態(tài)會轉(zhuǎn)換成Content。當(dāng)你寫完文檔后,調(diào)用WriteEndDocument方法,狀態(tài)就會返回為Start,直到你開始寫另一個文檔或者把Writer關(guān)掉。
Figure 7 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 1.0 document
Start
The writer is in an initial state, awaiting for a write call to be issued
Writer 把輸出文本存在內(nèi)部的一個緩沖區(qū)內(nèi)。一般情況下,緩沖區(qū)會被刷新或者被清除,當(dāng)Writer被關(guān)閉前XML文本應(yīng)該要寫出。在任何時你都可以通過調(diào)用Flush方法清空緩沖區(qū),把當(dāng)前的內(nèi)容寫到流中(通過BaseStream屬性暴露流),然后釋放部分占用的內(nèi)存,Writer仍保持為打開狀態(tài)(open state),可以繼續(xù)操作。注意,雖然寫了部分的文檔內(nèi)容,但是在Writer沒有關(guān)閉前其它的程序是不能處理該文檔的。
可以用兩種方法來寫屬性節(jié)點。第一種方法是用WriteStartAtribute方法去創(chuàng)建一個新的屬性節(jié)點,更新Writer的狀態(tài)。接著用WriteString方法設(shè)置屬性值。寫完后,用WriteEndElement方法結(jié)束該節(jié)點。另外,你也可以用WriteAttributeString方法去創(chuàng)建新的屬性節(jié)點,當(dāng)writerr的狀態(tài)為Element時,WriterAttributeString開始工作,它單獨(dú)創(chuàng)建一個屬性。同樣的,WriteStartElement方法寫節(jié)點的開始標(biāo)簽(<),然后你可以隨意的設(shè)置節(jié)點的屬性和文本內(nèi)容。元素節(jié)點的閉標(biāo)簽都帶”/ >”。如果想寫閉標(biāo)簽可以用WriteFullEndElement方法來寫。
?wèi)?yīng)該避免傳送給寫方法的文本中包含敏感的標(biāo)記字符,例如小于號(<)。用WriteRaw方法寫入流的字符串不會被解析,我們可以用它來對xml文檔寫入特殊的字符串。下面的兩行代碼,第一行輸出的是”<”,第二行輸出”<”:
writer.WriteString("<");
writer.WriteRaw("<");
[page_break]讀寫流
有趣的是,reader(閱讀器)和writer類提供了基于Base64 和BinHex編碼的讀寫數(shù)據(jù)流的方法。WriteBase64 和 WriteBinHex方法的功能與其它的寫方法的功能存在著細(xì)微的差別。它們都是基于流的,這兩個方法的功能像一個byte數(shù)組而不是一個string。下面的代碼首先把一個string轉(zhuǎn)換成一個byte數(shù)組,然后把它們寫成一個Base64 編碼流。Encoding類的GetBytes靜態(tài)方法完成轉(zhuǎn)換的任務(wù):
writer.WriteBase64(
Encoding.Unicode.GetBytes(buf),
0, buf.Length*2);
圖八中代碼演示了把一個string數(shù)據(jù)轉(zhuǎn)換為Base64 編碼的XML流。圖九是輸出的結(jié)果。
Figure 8 Persisting a String Array as Base64
using System;
using System.Text;
using System.IO;
using System.Xml;
class MyBase64Array
{
public static void Main(String[] args)
{
string outputFileName = "test64.xml";
if (args.Length > 0)
outputFileName = args[0]; // file name
// 把數(shù)組轉(zhuǎn)換成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);
//使子元素根據(jù) Indentation 和 IndentChar 設(shè)置縮進(jìn)。此選項只對元素內(nèi)容進(jìn)行縮進(jìn)
xmlw.Formatting = Formatting.Indented;
//書寫版本為“1.0”的 XML 聲明
xmlw.WriteStartDocument();
//寫出包含指定文本的注釋 。
xmlw.WriteComment("Array to Base64 XML");
//開始寫出array節(jié)點
xmlw.WriteStartElement("array");
//寫出具有指定的前綴、本地名稱、命名空間 URI 和值的屬性
xmlw.WriteAttributeString("xmlns", "x", null, "dinoe:msdn-mag");
// 循環(huán)的寫入array的子節(jié)點
foreach(string s in theArray)
{
//寫出指定的開始標(biāo)記并將其與給定的命名空間和前綴關(guān)聯(lián)起來
xmlw.WriteStartElement("x", "element", null);
//把S轉(zhuǎn)換成byte[]數(shù)組, 并把byte[]數(shù)組編碼為 Base64 并寫出結(jié)果文本,要寫入的字節(jié)數(shù)為s總長度的2倍,一個string占的字節(jié)數(shù)是2字節(jié)。
xmlw.WriteBase64(Encoding.Unicode.GetBytes(s), 0, s.Length*2);
//關(guān)閉子節(jié)點
xmlw.WriteEndElement();
}
//關(guān)閉根節(jié)點,只有兩級
xmlw.WriteEndDocument();
// 關(guān)閉writer
xmlw.Close();
// 讀出寫入的內(nèi)容
XmlTextReader reader = new XmlTextReader(filname);
while(reader.Read())
{
//獲取節(jié)點名為element的節(jié)點
if (reader.LocalName == "element")
{
byte[] bytes = new byte[1000];
int n = reader.ReadBase64(bytes, 0, 1000);
string buf = Encoding.Unicode.GetString(bytes);
Console.WriteLine(buf.Substring(0,n));
}
}
reader.Close();
}
}
Figure 9 String Array in Internet Explorer
Reader類有專門的解釋Base64和BinHex編碼流的方法。下面的代碼片斷演示了怎么樣用XmlTextReader類的ReadBase64方法解析用Base64和BinHex編碼集創(chuàng)建的文檔。
XmlTextReader reader = new XmlTextReader(filename);
while(reader.Read()) {
if (reader.LocalName == "element") {
byte[] bytes = new byte[1000];
int n = reader.ReadBase64(bytes, 0, 1000);
string buf = Encoding.Unicode.GetString(bytes);
Console.WriteLine(buf.Substring(0,n));
}
}
reader.Close();
從byte型轉(zhuǎn)換成string型是通過Encoding類的GetString方法實現(xiàn)的。盡管我只介紹了基于Base64編碼集的代碼,但是可以簡單的用BinHex替換方法名就可以實現(xiàn)讀基于BinHex編碼的節(jié)點內(nèi)容(用ReadBinHex方法)。這個技巧也可以用于讀任何用byte數(shù)據(jù)形式表示的二進(jìn)制數(shù)據(jù),尤其是image類型的數(shù)據(jù)。