《WCF技術內幕》23:第2部分_第5章_消息:XmlDictionaryReader和回到Message
XmlDictionaryReader類型
XmlDictionaryReader抽象類型繼承自System.Xml.XmlReader,因此繼承了很 多XmlReader的特性。和XmlReader 一樣,XmlDictionaryReader定義了幾個工廠 方法,他們返回的是XmlDictionaryReader的子類型的實例。更確切地說, XmlDictionaryReader包裝了一個Stream,並定義了許多以Read開頭的方法。因 此,由於繼承的層次關系,使用 XmlDictionaryReader和XmlReader很相似。
和XmlReader不同,XmlDictionaryReader的目的是為了讀取序列化的和編碼 過的XML Infosets ,並且可以由選擇地借助XmlDictionary來實現語義壓縮的反 向處理。作用上,XmlDictionaryReader 和XmlDictionaryWriter正好相反,並 且2個類型的對象模型非常相似。讓我們從XmlDictionaryReader的創建方法開始 學習,然後詳細研究如何使用它的Read方法。因為XmlDictionaryReader和 XmlDictionaryWriter的相似性,本節會比上一節XmlDictionaryWriter要簡短一 些。
創建一個XmlDictionaryReader對象
XmlDictionaryReader類型定義了幾個工廠方法,所有這些方法都直接或者間 接地,接受一個Stream 或Byte[]的引用。通常來說,面向stream的方法與面向 buffer的方法很相似。絕大部分,這些工廠方法都是重載一下4個方法: CreateDictionaryReader, CreateTextReader, CreateMtomReader和 CreateBinaryReader,它們的行為與XmlDictionaryWriter的相同名字工廠方法 對應。為了避免重復,我們將會關注XmlDictionaryReader的工廠方法的顯著特 性上。
幾個工廠方法接受一個Stream的引用,這些面向stream的工廠方法使用的其 它參數包含一個XmlDictionaryQuotas 對象的引用和一個 OnXmlDictionaryReaderClose委托。在所有的情況下,前者調用後者,為 XmlDictionaryQuotas 和OnXmlDictionaryReaderClose參數傳遞null引用。
XmlDictionaryQuotas類型是一個狀態容器,它用來定義與XML反序列化相關 的重要的閥值。例如,這個類型定義反序列化中用到的節點深度的最大值、反序 列化最大的String長度、消息體的最大數組長度等等【老徐備注1】。
OnXmlDictionaryReaderClose委托在XmlDictionaryReader 的Close方法幾乎 結束的時候調用。當這個委托被激活以後,XmlDictionaryReader的大部分狀態 被設置為null。因此,這個委托可以用來作為提醒機制(很像一個事件event) ,但不會提供XmlDictionaryReader狀態相關的任何有價值的信息(除非Null是 有價值的)。Message編碼器使用OnXmlDictionaryReaderClose委托去把 XmlDictionaryReader對象放到對象池。這些編碼器依賴 OnXmlDictionaryReaderClose委托這樣的提醒,它會返回一個資源池裡的 XmlDictionaryReader實例。
一下代碼演示了如何實例化一個XmlDictionaryReader對象:
private static void CreateTextReader() {
Console.WriteLine("==== Creating XML Dictionary Text Reader ====");
MemoryStream stream = new MemoryStream();
// create an XmlDictionaryWriter and serialize/encode some XML
XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(stream,
Encoding.BigEndianUnicode, false);
writer.WriteStartDocument();
writer.WriteElementString("SongName",
"urn:ContosoRockabilia",
"Aqualung");
writer.Flush();
stream.Position = 0;
// create an XmlDictionaryReader to decode/deserialize the XML
XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(
stream, Encoding.BigEndianUnicode, new XmlDictionaryReaderQuotas(),
delegate { Console.WriteLine("closing reader"); } );
reader.MoveToContent();
Console.WriteLine("Read XML Content:{0}",reader.ReadOuterXml ());
Console.WriteLine("about to call reader.Close()");
reader.Close();
Console.WriteLine("reader closed");
}
當以上代碼執行的時候,就會產生以下輸出:
==== Creating XML Dictionary Text Reader ====
Read XML Content:
<SongName xmlns="urn:ContosoRockabilia">Aqualung</SongName>
about to call reader.Close()
closing reader
reader closed
著重指出一下,XmlDictionaryReader的其它工廠方法接受的參數和 XmlDictionaryWriter的工廠方法幾乎一一對應。這些參數與在 XmlDictionaryWriter類型裡的作用一樣。
借助XmlDictionary生成XML數據
既然已經看了如何實例化XmlDictionaryWriter和XmlDictionaryReader,現 在我們就學習一下如何使用XmlDictionary讀取二進制編碼的XML。如下面的代碼 所示,這與你在XmlDictionaryWriter裡看到的一樣:
MemoryStream stream = new MemoryStream();
// create the dictionary and add dictionary strings
XmlDictionary dictionary = new XmlDictionary();
List<XmlDictionaryString> stringList = new List<XmlDictionaryString>();
stringList.Add(dictionary.Add("SongName"));
stringList.Add(dictionary.Add("urn:ContosoRockabilia"));
// use an XmlDictionaryWriter to serialize some XML使用 XmlDictionaryWriter序列化一些XMl
using (XmlDictionaryWriter writer =
XmlDictionaryWriter.CreateBinaryWriter(stream, dictionary, null)) {
// write using the dictionary - element name, namespace, value
writer.WriteElementString(stringList[0], stringList[1], "Aqualung");
writer.Flush();
Console.WriteLine("Using Dictionary, wrote {0} bytes",
stream.Position);
stream.Position = 0;
Byte[] bytes = stream.ToArray();
Console.WriteLine(BitConverter.ToString(bytes));
// create an XmlDictionaryReader passing the Stream創建 XmlDictionaryReader並傳遞Stream
// and an XmlDictionary
XmlDictionaryReader reader =
XmlDictionaryReader.CreateBinaryReader(stream, dictionary, new
XmlDictionaryReaderQuotas());
reader.Read();
Console.WriteLine("data read from stream:\n{0}\n",
reader.ReadOuterXml());
}
當代碼執行的時,輸出一下結果:
XmlDictionaryWriter (Binary w/dictionary) wrote 14 bytes
42-00-0A-02-99-08-41-71-75-61-6C-75-6E-67
data read from stream:
<SongName xmlns="urn:ContosoRockabilia">Aqualung</SongName>
注意到傳遞到CreateBinaryWriter方法上的XmlDictionary和傳遞到 XmlDictionaryReader方法上的XmlDictionary是一個對象的引用。誠然,傳遞同 一個對象的引用的是有些粗糙但是能保證XmlDictionaryWriter 和 XmlDictionaryReader使用一個詞匯表,但是它說明了XmlDictionaryReader可以 解釋XmlDictionaryWriter使用XmlDictionary壓縮的數據。
回到Message類型
既然我們已經研究過了序列化和編碼一個Message的相關類型,現在該重新關 注一下Message類型了。Message對象模型初略估計包含45個public或protected 的成員。這些成員中包含返回Message實例的工廠方法、序列化Message的方法、 反序列化Message的方法、返回Message信息的屬性、處理Message頭部的屬性和 清理Message的方法。
【老徐備注】
1.為交換的 Soap 消息指定復雜性約束的 XmlDictionaryReaderQuotas。下 面的備注部分中提供了這些約束的默認值。
這些復雜性約束可以抵御某種類型的拒絕服務 (DOS) 攻擊,這些攻擊試圖利 用消息復雜性來占用終結點處理資源。表達這些約束及其默認值的屬性如下所示 :
字典讀取器最重要的安全功能是配額。必須為字典讀取器工廠方法指定配額 實例。默認構造函數創建“安全”默認值(與編碼默認值相同),並且類具有靜 態 Max 屬性,用於創建不帶配額的讀取器。
5.maxDepth="32" 最大節點深度
6.maxStringContentLength="8192" 最大內容長度
7.maxArrayLength="16384"最大數組長度
8.maxBytesPerRead="4096" 最大每次讀取長度
9.maxNameTableCharCount="16384"最大NameTableChar的數量
我曾經在MSDN WCF中文論壇解釋過,WCF使用到的所有的Max的作用。
http://social.microsoft.com/Forums/zh-CN/wcfzhchs/thread/73f31b97- bef5-47c6-b50e-d0d3140d8efb