.NET提供了很多序列化對象的方法,瞭解他們之間的區別才能更好地確定使用哪一種序列化方式並正確地使用。本文從下麵幾個方面對標題中的三種序列化方法進行了分析。 範圍:Property Or Field Or Both 可見性:Public or Private Or All 可訪問性:Readonly ...
.NET提供了很多序列化對象的方法,瞭解他們之間的區別才能更好地確定使用哪一種序列化方式並正確地使用。本文從下麵幾個方面對標題中的三種序列化方法進行了分析。
- 範圍:Property Or Field Or Both
- 可見性:Public or Private Or All
- 可訪問性:Readonly Property
- 回調:指OnSerializing, OnSerialized, OnDeserializing, OnDeserialized這些回調。
- 包含迴圈引用的對象
- 包含Dictionary的對象
- 亂序讀(XML)
- 自定義序列化結果。
- 對被序列化的類有沒有什麼Attribute要求。有時被序列化的對象位於第三方庫中,無法修改源代碼,如果要序列化這樣的對象,就不能使用一定要有Attribute的Serializer。
先把結果整理出來再分析:代碼可以從這裡下載,所寫測試可能並不全面,所以如果有什麼錯誤,歡迎大家指出,不然要誤人子弟了。不過有這篇更誤人子弟的文章殿底(Google top 1),我相信這個表不會更糟。
XmlSerializer | DataContractSerializer | BinaryFormatter | |
範圍 | Both | Both | Field Only |
可見性 | Public Only | All | All |
只讀屬性 | 不支持 | 支持,但要加Attribute | N/A |
回調支持 | 不支持 | 支持 | 支持 |
迴圈引用 | 不支持 | 支持,但要加Attribute | 支持 |
Dictionary | 不支持 | 支持,專有格式 | 支持 |
亂序讀 | 支持 | 不支持 | 不支持 |
自定義格式 | 支持,要加Attribute | 不支持Xml Attribute | 通過介面 |
Attribute要求 | 不必要 | 不必要 | 必須有Serializable |
有幾點需要解釋一下。
性能?大小?
在我寫的幾個測試用例中,無論對於簡單對象,還是嵌套對象,還是數組重覆引用對象,這幾個Serializer的性能和大小基本在一個數量級上。他們之間功能上的差異遠大於他們性能上的差異。這個時候,使用哪個方法的決策應該取決於功能及其約束,而不是性能。如果你在意性能,應該用第三方庫,如Google的protobuf-net。無論從性能、序列化結果的大小還是相容性上,protobuf-net比這三者都要優秀,當然,not human readable。
Attribute要求
不少同類文章都聲稱XmlSerializer和DataContractSerializer都要求在類上加相應的Attribute。但事實上並不是這樣。只有當你想要改變預設的序列化結果時,Attribute才是必要的。
最常見的一個錯誤就是,給XmlSerializer要用到的類加Serializable Attribute。這個Attribute其實是給BinaryFormatter和SoapFormatter用的,一般用在Remoting和Messaging上。
亂序讀
亂序讀的意思是,Deserialize的過程中,不對Xml節點的順序做要求。Xml本身就是一種標記性的,帶有聲明性的語言,多數基於Xml的網路協議都不會對Xml節點的順序做要求。比如常見的RSS,ATOM,OPDS等。
WCF使用的DataContractSerializer要求Xml節點順序要麼嚴格指定,要麼按字母順序排列,或許有性能上考慮,但是WCF的定位才是允許DataContractSerializer嚴格要求Xml節點順序的根本原因。WCF本身是用於通信的,其對序列化的定位,僅僅只是消息傳遞的手段,消息格式的嚴格定義並沒有什麼不好,而且調用方的代碼都可以通過WSDL來生成,自然也就更不會出現解析時的問題。所以不支持亂序讀完全是由其功能設定決定的。
XmlSerializer,在我看來可以做為Object Xml Mapping的工具,當然要儘可能支持所有Xml的標準。
自定義的支持
XmlSerializer和DataContractSerializer都支持IXmlSerializer介面。通過這個介面DataContractSerializer甚至可以支持Attribute,但是,這其實已經不是在使用DataContractSerializer的序列化機制了。而且在實際項目中也不具備可操作性。所以在上表中,還是把DataContractSerializer標記為不支持Xml Attribute。
還有一些常見的介面,如ISerializable和IDeserializationCallback是給BinaryFormatter用的,以支持自定義序列化的過程。這裡我很奇怪為了BinaryFormatter已經支持了OnDeserialized Attribute,為什麼還有這麼個介面來提供相似的功能。
一些小的細節
兩個Xml序列化方法還有一些小的挺有意思的差別,我試圖猜測產生這種差異的原因,卻沒想出來,或許是一些微軟員工不為人知的設計理念,也許只是寫代碼時的順心而為罷了。
- XmlSerializer不序列化Null值,DataContractSerializer預設會序列化Null值。
- DataContractSerializer與Attribute:對沒有任何Attribute的類,序列化所有Public的可讀可寫Property和Field;對僅僅加了Serializable Attribute的類,序列化所有可見性的Field(為了和BinaryFormatter行為一致嗎);如果僅僅加了DataContract Attribute,則什麼都不會序列化出來,一定要加DataMember。
- DataContract支持序列只讀屬性,但是屬性上要加DataMember.
- DataContractSerializer,僅僅加DataMember而不加DataContract會出異常。
- DataContractSerializer與ISerializable介面不相容,直接拋異常。
- DataContractSerializer支持IXmlSerializer介面,但是實現了這個介面,就不能加DataContract了。否則拋異常。這是什麼道理?
- XmlSerializer支持亂序讀的代價是,你不能控制你自己的生成的Xml的節點的順序。也就是說ElementName Attribute中不能指定Order。
- BinaryFormatter要求被序列化的類必須“滿城盡帶Serializable”。所以如果你改不了源代碼,序列化不了就是序列化不了。而XmlSerializable,你盡可以通過繼承的方式,把Internal和Protected的Property序列化出來。
小結
基於上面的客觀結果,相信你已經有了自己的判斷。我個人意見,DataContractSerializable就用在它應該用的地方吧,如果不是用WCF,還是不要用它了,它的序列化結果有一些微軟專屬的東西。對於來自網路的鬆散Xml介面數據,XmlSerializer是不二之選。如果想把對象完整地保存下來(數據與狀態),同時又不需要被人看。那就用BinaryFormatter吧。如果對性能或是數據大小要求比較高,那這三個就都能用,用protobuff吧。
補充
這些信息算是一種經驗,但是絕對不適合作面試或是筆試題用。