8.10 序列化(第二版)
-
.NET框架提供了三种主要的序列化技术,如下:
- 数据协定(Data Contract) 序列化;
- XML 序列化;
- 运行时 序列化(二进制和SOAP)
-
DO
:在设计新的类型时考虑到序列化。如果类型实例需要持久化,或需要传输,则设计时要考虑如何序列化。
1 选择要支持的序列化技术
-
DO
:优先支持 数据协定 序列化。
-
CONSIDER
:如果需要 控制序列化方式 ,可以只支持XML序列化,或同时支持数据协定序列化和XML序列化。
-
CONSIDER
:如果需要跨.NET Remoting的边界传输类型,应实现 运行时序列化 。
2 对数据协定序列化的支持
-
CONSIDER
:如果类型会被用于部分可信(partial trust)环境,序列化成员要定义为 公有 。- 完全可信(full trust)环境下,Data Contract可以对所有成员进行序列化;部分可信环境下,仅对公有成员进行序列化。
-
DO
:需要序列化的属性 必须有 getter和setter。- 如果仅用于完全可信的环境中,getter和setter可以是非公有的。
-
DO
:使用序列化回调函数在反序列化时初始化实例。
序列化回调函数通过Attribute标注,包括:-
OnDeserializedAttribute
反序列化后 触发 -
OnDeserializingAttribute
反序列化前 触发 -
OnSerializingAttribute
序列化前 触发 -
OnSerializedAttribute
序列化后 触发
我们可以在这些回调函数中进行一些附加操作
-
[DataContract] class Person { [DataMember] string lastName; [DataMember] string firstName; string fullName; public Person(string firstName, string lastName) { this.lastName = lastName; this.firstName = firstName; fullName = firstName + " " + lastName; } public string FullName => fullName; [OnDeserialized] void OnDeserialized(StreamingContext context) { fullName = firstName + " " + lastName; } }
-
-
CONSIDER
:使用 KnowTypeAttribute
标注反序列化时会遇到的具体来类型。
KnowTypeAttribute
有两个构造函数,KnownTypeAttribute(Type type)
传入类型,KnownTypeAttribute(string methodName)
传入返回Type[]
的静态方法:[KnownType("GetKnownTypes")] [DataContract] public class Person { ... public static Type[] GetKnownTypes() { return new[] { typeof(DateTime, School, ... ) }; } }
-
DO
:创建、改变序列化类型时要考虑向后、向前兼容性。Data Contract反序列化与以下因素相关:
- 数据成员的名字
- 类型
- 顺序
改变这些内容会导致反序列化失败。如果要修改,应进行特殊处理(可以通过DataMember的可选参数进行标注)。
-
CONSIDER
:可以通过实现 IExtensibleDataObject 支持新老版本的双向转换。[DataContract] class Person : IExtensibleDataObject { ... ExtensionDataObject serializationData; ExtensionDataObject IExtensibleDataObject.ExtensionData { get => serializationData; set => serializationData = value; } }
3 对XML序列化的支持
DataContract 是.NET框架中主要(默认)序列化技术,但也存在一些不支持的场景(如多维数组)。此时可以使用XML序列化,我们可以通过它完全控制序列化过程。
-
AVOID
:除非有强烈的理由控制输出的内容,否则不要使用 XML序列化 。我们可以通过特性对生成的XML加以控制:
public class Address { [XmlAttribute] // 序列化时作为XML属性,而非元素 public string Name { get { return "John Smith";} set { } } [XmlElement(ElementName = "StreetLine")] // 指明元素名称 public string Street = "1 Some Street"; }
-
CONSIDER
:应用XML序列化Attribute后,如果生成的XML内容仍不满足要求,可以实现 IXmlSerializable 接口。该接口有两个方法,
ReadXml
和WriteXml
,可以用它们完全控制生成的XML内容。如果上述接口还不够,可以给类型添加XmlSchemaProviderAttribute
,用于对XML架构(XML schema)进行控制。
4 对运行时序列化的支持
-
CONSIDER
:如果类型会被用于.NET Remoting,考虑支持 运行时序列化 。例如:System.AddIn用到了.NET Remoting,因此System.AddIn的外接程序之间传输的所有类型都必须支持运行时序列化。
[Serializable] // 通过添加该特性便可支持运行时序列化。 public class Person { ... }
-
CONSIDER
:如果想要完全控制序列化的整个过程,实现 ISerializable 接口。该接口包含一个
ISerializable.GetObjectData
方法,用于序列化,我们还需定义一个构造函数,用于反序列化。该构造函数应定义为protected,如果是密封类型,则是private。
接口方法要显式实现,并标注SecurityAction.LinkDemand
,保证只有完全可信的代码和运行时序列化程序才能访问该成员;构造函数要定义为protected,有两个参数。
具体实现参见示例代码。[Serializable] public class Person : ISerializable { string fullName; public string FullName { get => fullName; set => fullName = value; } public Person() { } // 反序列化构造函数 protected Person(SerializationInfo info, StreamingContext context) { if (info == null) throw new System.ArgumentNullException("info"); fullName = (string)info.GetValue("name", typeof(string)); } // 接口引入的序列化方法。 [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)] void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) { if (info == null) throw new System.ArgumentNullException("info"); info.AddValue("name", fullName); } }
标签:XML,string,第二,8.10,fullName,序列化,public,CONSIDER From: https://www.cnblogs.com/hihaojie/p/18669609/810-serialization-second-edition-zoesle