惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

WordPress大学
WordPress大学
T
Threatpost
IT之家
IT之家
腾讯CDC
博客园 - 【当耐特】
博客园 - 聂微东
月光博客
月光博客
小众软件
小众软件
博客园 - 三生石上(FineUI控件)
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
酷 壳 – CoolShell
酷 壳 – CoolShell
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - Franky
博客园 - 叶小钗
S
Security @ Cisco Blogs
雷峰网
雷峰网
N
News and Events Feed by Topic
Attack and Defense Labs
Attack and Defense Labs
Forbes - Security
Forbes - Security
Application and Cybersecurity Blog
Application and Cybersecurity Blog
S
SegmentFault 最新的问题
NISL@THU
NISL@THU
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
L
Lohrmann on Cybersecurity
Project Zero
Project Zero
www.infosecurity-magazine.com
www.infosecurity-magazine.com
宝玉的分享
宝玉的分享
阮一峰的网络日志
阮一峰的网络日志
The Last Watchdog
The Last Watchdog
Apple Machine Learning Research
Apple Machine Learning Research
W
WeLiveSecurity
C
Cisco Blogs
T
Tenable Blog
Schneier on Security
Schneier on Security
爱范儿
爱范儿
美团技术团队
V2EX - 技术
V2EX - 技术
V
V2EX
Google Online Security Blog
Google Online Security Blog
Last Week in AI
Last Week in AI
Security Archives - TechRepublic
Security Archives - TechRepublic
The Hacker News
The Hacker News
Recent Commits to openclaw:main
Recent Commits to openclaw:main
S
Schneier on Security
T
The Exploit Database - CXSecurity.com
博客园_首页
H
Hacker News: Front Page
量子位
N
News | PayPal Newsroom
大猫的无限游戏
大猫的无限游戏

博客园 - 南柯之石

用户中心 - 博客园 自引用泛型模式分析 一定间隔时间下重复执行一个函数的几个方法 为什么.NET Framework就没有个专门的P/Invoke Library? Which would you perfer? "easy and fast and works well" or "hard and time consuming and error prone" 在ListView的GroupItem头中显示每列的Summary 小例子背后的大道理——用户需求+设计原则+正确应用 =设计方案 GET和POST有什么区别?及为什么网上的多数答案都是错的。 小例子背后的大道理——Adapter模式详解 小例子背后的大道理——从DIP中“倒置”的含义说接口的正确使用 使用WPF开发的扫雷游戏,双系统主题复刻版 技术误用带来的技术偏见及所谓的“经验” 从一个UI交互设计师的讲座说开去 NoSQL和MemeryCache的出现意味着传统数据库使用方式的变革吗? 不使用反射进行C#属性的运行时动态访问 一次模块划分的争论及其结局 电子书籍质量保证事件分析 在SQL Server中调用.NET程序集 T-SQL 操作XML示例
XmlSerializer, DataContractSerializer 和 BinaryFormatter区别与用法分析
南柯之石 · 2012-05-12 · via 博客园 - 南柯之石

.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序列化方法还有一些小的挺有意思的差别,我试图猜测产生这种差异的原因,却没想出来,或许是一些微软员工不为人知的设计理念,也许只是写代码时的顺心而为罢了。

  1. XmlSerializer不序列化Null值,DataContractSerializer默认会序列化Null值。
  2. DataContractSerializer与Attribute:对没有任何Attribute的类,序列化所有Public的可读可写Property和Field;对仅仅加了Serializable Attribute的类,序列化所有可见性的Field(为了和BinaryFormatter行为一致吗);如果仅仅加了DataContract Attribute,则什么都不会序列化出来,一定要加DataMember。
  3. DataContract支持序列只读属性,但是属性上要加DataMember.
  4. DataContractSerializer,仅仅加DataMember而不加DataContract会出异常。
  5. DataContractSerializer与ISerializable接口不兼容,直接抛异常。
  6. DataContractSerializer支持IXmlSerializer接口,但是实现了这个接口,就不能加DataContract了。否则抛异常。这是什么道理?
  7. XmlSerializer支持乱序读的代价是,你不能控制你自己的生成的Xml的节点的顺序。也就是说ElementName Attribute中不能指定Order。
  8. BinaryFormatter要求被序列化的类必须“满城尽带Serializable”。所以如果你改不了源代码,序列化不了就是序列化不了。而XmlSerializable,你尽可以通过继承的方式,把Internal和Protected的Property序列化出来。

小结

基于上面的客观结果,相信你已经有了自己的判断。我个人意见,DataContractSerializable就用在它应该用的地方吧,如果不是用WCF,还是不要用它了,它的序列化结果有一些微软专属的东西。对于来自网络的松散Xml接口数据,XmlSerializer是不二之选。如果想把对象完整地保存下来(数据与状态),同时又不需要被人看。那就用BinaryFormatter吧。如果对性能或是数据大小要求比较高,那这三个就都能用,用protobuff吧。

补充

这些信息算是一种经验,但是绝对不适合作面试或是笔试题用。

2012年6月2日:

发现一个不错的总结:

http://www.codeproject.com/Articles/255684/Create-and-Consume-RESTFul-Service-in-NET-Framewor