前言
- WCF包含很多封裝的內部機制,這些是我們在編寫程序時不會經常看到的。比如上一篇講解的Message。這一篇我將講解WCF的另一種內部機制,WCF的序列化和反序列化。通常我們在編寫WCF服務程序的時候,我們並沒有手動對WCF的數據傳遞做序列化和反序列化的操作,這是因為WCF默認通過序列化引擎DataContractSerializer幫我們做了這些操作,使得開發人員只需關注數據對象定義本身(比如數據協定、消息協定),而不必關注數據對象的在WCF傳輸時的序列化機制。
- DataContractSerializer 可在 .NET Framework 對象和 XML 之間進行雙向轉換。在對 .NET Framework 對象進行序列化時,序列化程序了解各種序列化編程模型,包括新的數據協定模型。此序列化程序支持下列類型:
- 基元類型(如:整數、字符串和字節數組)以及某些特殊類型(如 XmlElement 和 DateTime),這些特殊類型也被視為基元類型。
- 數據協定類型(用 DataContractAttribute 屬性標記的類型)。
- 用 SerializableAttribute 屬性標記的類型,包括實現 ISerializable 接口的類型。
- 實現 IXmlSerializable 接口的類型。
- 許多常見集合類型,包括許多泛型集合類型。
使用DataContractSerializer序列化和反序列化
- 對對象進行序列化最基本的方法是將其傳遞到 WriteObject 方法。該方法有三個重載,每個重載分別用於寫入到 Stream、XmlWriter 或 XmlDictionaryWriter。使用 Stream 重載時,輸出是采用 UTF-8 編碼的 XML。使用 XmlDictionaryWriter 重載時,序列化程序會針對二進制 XML 優化其輸出。參考代碼如下:
User user = new User { ID = 1, Name = "JACK", Age = 20, Nationality = "CHINA" }; FileStream writer = new FileStream(fileName, FileMode.Create); DataContractSerializer ser =new DataContractSerializer(typeof(User)); ser.WriteObject(writer, user); writer.Close();
- 對對象進行反序列化的最基本的方式是調用 ReadObject 方法重載之一。該方法有三個重載,每個重載分別用於讀取 XmlDictionaryReader、XmlReader 或 Stream。請注意,Stream 重載將創建不受任何配額保護的文本 XmlDictionaryReader,此重載僅應用於讀取受信任的數據。還請注意,必須將 ReadObject 方法返回的對象強制轉換為適當的類型。參考代碼如下:
FileStream fs = new FileStream(fileName,FileMode.Open); XmlDictionaryReader reader =XmlDictionaryReader.CreateTextReader(fs, new XmlDictionaryReaderQuotas()); DataContractSerializer ser = new DataContractSerializer(typeof(User)); User deserializedUser =(User)ser.ReadObject(reader, true);
使用DataContractSerializer序列化和反序列化示例如下:
- 創建一個名稱為WcfDataContractSerializer的解決方案,添加一個名稱為WcfDataContractSerializer的控制台引用程序。修改Program.cs代碼如下:
using System; using System.Runtime.Serialization; using System.IO; using System.Xml; namespace WcfDataContractSerializer { class Program { static void Main(string[] args) { try { WriteObject("DataContractSerializerExample.xml"); ReadObject("DataContractSerializerExample.xml"); } catch (SerializationException serExc) { Console.WriteLine("序列化失敗"); Console.WriteLine(serExc.Message); } catch (Exception exc) { Console.WriteLine( "序列化操作失敗: {0} StackTrace: {1}", exc.Message, exc.StackTrace); } finally { Console.WriteLine("按 <Enter> 鍵退出...."); Console.ReadLine(); } } public static void WriteObject(string fileName) { Console.WriteLine("創建User對象並序列化它"); User user = new User { ID = 1, Name = "JACK", Age = 20, Nationality = "CHINA" }; FileStream writer = new FileStream(fileName, FileMode.Create); DataContractSerializer ser =new DataContractSerializer(typeof(User)); ser.WriteObject(writer, user); writer.Close(); Console.WriteLine("序列化User對象成功,請到程序Bin目錄下查看DataContractSerializerExample.xml文件"); } public static void ReadObject(string fileName) { Console.WriteLine("反序列化實例對象"); FileStream fs = new FileStream(fileName,FileMode.Open); XmlDictionaryReader reader =XmlDictionaryReader.CreateTextReader(fs, new XmlDictionaryReaderQuotas()); DataContractSerializer ser = new DataContractSerializer(typeof(User)); User deserializedUser =(User)ser.ReadObject(reader, true); reader.Close(); fs.Close(); Console.WriteLine("{0,-10}{1,-10}{2,-10}{3,-10}", "ID", "Name", "Age", "Nationality"); Console.WriteLine("{0,-10}{1,-10}{2,-10}{3,-10}", deserializedUser.ID.ToString(), deserializedUser.Name.ToString(), deserializedUser.Age.ToString(), deserializedUser.Nationality.ToString()); } } [DataContract] public class User { [DataMember] public int ID { get; set; } [DataMember] public string Name { get; set; } [DataMember] public int Age { get; set; } [DataMember] public string Nationality { get; set; } } }
- 運行程序結果如下:
打開程序Bin目錄下的DataContractSerializerExample.xml,顯示結果如下:
使用DataContractJsonSerializer序列化和反序列化
- 使用DataContractJsonSerializer可以將對象轉化為Json格式,如果你習慣使用Json,那么可以使用該序列化引擎作為對象的傳輸時的序列化工具。該序列化引擎和DataContractSerializer一樣,具有WriteObject和ReadObject方法,具體用法參照示例。
- 注意:該序列化引擎只會對WCF的消息正文進行Json序列化操作,也就是說總個消息還是基於SOAP的XML格式,只有Body部分變成了JSON字符串。
使用DataContractJsonSerializer序列化和反序列化示例
- 在先前的解決方案上添加名稱為WcfDataContractJsonSerializer的控制台應用程序,修改Program.cs的代碼如下:
using System; using System.Runtime.Serialization; using System.IO; using System.Runtime.Serialization.Json; using System.Text; namespace WcfDataContractJsonSerializer { class Program { static void Main(string[] args) { try { Console.WriteLine("創建User對象並序列化它"); User user = new User { ID = 1, Name = "JACK", Age = 20, Nationality = "CHINA" }; string jsonString = Serialize<User>(user); Console.WriteLine(jsonString); Console.WriteLine("反序列化實例對象"); User deserializedUser = Deserialize<User>(jsonString); Console.WriteLine("{0,-10}{1,-10}{2,-10}{3,-10}", "ID", "Name", "Age", "Nationality"); Console.WriteLine("{0,-10}{1,-10}{2,-10}{3,-10}", deserializedUser.ID.ToString(), deserializedUser.Name.ToString(), deserializedUser.Age.ToString(), deserializedUser.Nationality.ToString()); } catch (SerializationException serExc) { Console.WriteLine("序列化失敗"); Console.WriteLine(serExc.Message); } catch (Exception exc) { Console.WriteLine( "序列化操作失敗: {0} StackTrace: {1}", exc.Message, exc.StackTrace); } finally { Console.WriteLine("按 <Enter> 鍵退出...."); Console.ReadLine(); } } public static string Serialize<T>(T item) { if (item == null) return string.Empty; var serializer = new DataContractJsonSerializer(item.GetType()); using (var ms = new MemoryStream()) { serializer.WriteObject(ms, item); var sb = new StringBuilder(); sb.Append(Encoding.UTF8.GetString(ms.ToArray())); return sb.ToString(); } } public static T Deserialize<T>(string jsonString) { if (string.IsNullOrEmpty(jsonString)) return default(T); var ser = new DataContractJsonSerializer(typeof(T)); using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(jsonString))) { T jsonObject = (T)ser.ReadObject(ms); return jsonObject; } } } [DataContract] public class User { [DataMember] public int ID { get; set; } [DataMember] public string Name { get; set; } [DataMember] public int Age { get; set; } [DataMember] public string Nationality { get; set; } } }
- 運行結果如下:
序列化格式大小對比
- 序列化格式的三種類型:XML文本類型、Json文本類型、二進制類型。接下來,我們通過一個小示例來看看這三種類型序列化時的字節大小。因為我們知道在網絡數據傳輸時,字節大小的傳遞顯得尤為重要呢。
- 在先前的解決方案中添加名稱為WcfSerializerCompare的控制台應用程序,修改Program.cs的代碼如下:
using System; using System.Runtime.Serialization; using System.IO; using System.Runtime.Serialization.Json; using System.Xml; namespace WcfSerializerCompare { class Program { static void Main(string[] args) { MemoryStream stream1 = new MemoryStream(); MemoryStream stream2 = new MemoryStream(); MemoryStream stream3 = new MemoryStream(); User user = new User { ID = 1, Name = "JACK", Age = 20, Nationality = "CHINA" }; DataContractSerializer xmlSerializer = new DataContractSerializer(typeof(User)); xmlSerializer.WriteObject(stream1, user); DataContractJsonSerializer jsonSerializer = new DataContractJsonSerializer(typeof(User)); jsonSerializer.WriteObject(stream2, user); DataContractSerializer binarySerializer = new DataContractSerializer(typeof(User)); XmlDictionaryWriter write = XmlDictionaryWriter.CreateBinaryWriter(stream3); binarySerializer.WriteObject(write, user); write.Flush(); Console.WriteLine("XML文本字節數大小為:{0}bytes", stream1.Length); Console.WriteLine("JSON文本字節數大小為:{0}bytes", stream2.Length); Console.WriteLine("二進制字節數大小為:{0}bytes", stream3.Length); Console.Read(); } } [DataContract] public class User { [DataMember] public int ID { get; set; } [DataMember] public string Name { get; set; } [DataMember] public int Age { get; set; } [DataMember] public string Nationality { get; set; } } }
- 運行結果如下:
從上面的示例可以看出,采用Json文本傳輸的格式為最小,只有53個字節數。