[MSDN]WCF(10)序列化

说明:本内容来自微软的webcast,讲师为徐长龙。为了用手机阅读方便点,抄录存为txt。

本次课程内容包括
- DataContractSerializer
- 序列化
- 反序列化
- XmlSerializer


DataContractSerializer
- WCF 包括新序列化引擎 DataContractSerializer。 DataContractSerializer 可在 .NET Framework 对象和 XML 之间进行双向转换
- 在对 .NET Framework 对象进行序列化时,序列化程序了解各种序列化编程模型,包括新的数据协定模型。
- 当对 XML 进行反序列化时,序列化程序使用 XmlReader 和 XmlWriter 类。在某些情况下(例如在使用WCF二进制XML格式时),序列化程序也支持 XmlDictionaryReader 和 XmlDictionaryWriter 类以使其能够生成优化的 XML。
- WCF还包括一个伴随序列化程序 NetDataContractSerializer 。 NetDataContractSerializer 与 BinaryFormatter 和 SOAPFormatter 序列化程序类似,因为它也发出 .NET Framework 类型名称作为序列化数据的一部分。当在序列化和反序列化结束阶段共享相同的类型时使用此序列化程序。
- DataContractSerializer 和 NetDataContractSerializer 都派生自公共基类 XmlObjectSerializer。

创建 DataContractSerializer 实例
- 指定跟类型
  - 根类型是序列化或反序列化实例的类型。 DataContractSerializer 有许多构造函数重载,但必须使用 type 参数提供至少一个根类型。
  - 为某个根类型创建的序列化程序不能用于序列化(或反序列化)其他类型,除非该类型是从根类型派生的。
  [DataContract]
  public class Person
  {}
  public class PurchaseOrder
  {}

  DataContractSerializer dcs = new DataContractSerializer(typeof(Person));
  // This can now be used to serializ/deserializ Person but not PurchaseOrder
- 指定默认根名称和命名空间
  - 通常,在对对象进行序列化时,将根据数据协定名称和命名空间确定最外面的 XML 元素的默认名称和命名空间。所有内部元素的名称将根据数据成员名称来确定,这些元素的命名空间是数据协定的命名空间。
  [DataContract(Name="PersonContract",Namespace="http://schemas.contoso.com")]
  public class Person2
  {
    [DataMember(Name="AddressMember")]
    public Address theAddress;
  }

  [DataContract(Name="AddressContract",Namespace="http://schemas.contoso.com")]
  public class Addres
  {
    [DataMember(Name="StreetMember")]
    public string street;
  }

  <PersonContract xmlns="http://schemas.contoso.com">
   <AddressMember>
    <StreetMember>123 main street</StreetMember>
   </AddressMember>
  </PersonContract>
- 设置最大对象配额
  - 一些 DataContractSerializer 构造函数重载具有 maxItemsInObjectGraph 参数。此参数确定序列化程序在单个 ReadObject 方法调用中序列化或反序列化的对象的最大数目。(该方法总是读取一个根对象,但此对象的数据成员中可以具有其他对象。这些对象有可以有其他对象,以此类推)默认值为65536。
  - 注意,当序列化或反序列化数组时,每个数组项都计为一个单独的对象。
- 往返行程
  - 在一次操作中对对象进行序列化和重新序列化时将发生往返行程。因此,往返行程是从 XML 到对象实例,然后再返回到 XML 流。
  - 一些 DataContractSerializer 构造函数重载具有 ignoreExtensionDataObject 参数,该参数默认设置为 false 。
  - 在此默认模式中,对于一个往返行程,可以将数据从数据协定的较新版本发送到较旧版本然后再返回到较新版本而不会出现任何损失,前提是数据协定实现 IExtensionDataObject 接口。例如,假设 Person 数据协定的版本1包含 Name 和 PhoneNumber 数据成员,并且版本2添加 Nickname 成员。如果在版本2发送信息到版本1时实现 IExtensionDataObject,则存储 Nickname 数据,并在再次序列化数据时再次发出这些数据,因此在往返行程中不会出现数据丢失。
- 启用和禁用往返行程
  - 要关闭往返行程,请不要实现 IExtensionDataObject 接口。如果您无法控制相应的类型,则将 ignoreExtensionDataObject 参数设置为 ture 也可以获得同样的效果。
- 对象图保留
  // Contruct a purchase order;
  Address adr = new Address();
  adr.street="123 Main st.";
  PurchaseOrder po = new PurchaseOrder();
  po.billTo = adr;
  po.shipTo = adr;

  <PurchaseOrder>
    <billTo><street>123 Main st.</street></billTo>
    <shipTo><street>123 Main st.</street></shipTo>
  </PurchaseOrder>
- 此方法具有以下可能不需要的特征:
  - 性能。复制数据的效率低。
  - 循环引用。如果对象引用自身,甚至通过其他对象引用自身,则通过复制进行序列化会导致无限循环。(如果发生这种状况,序列化程序将引发 SerializationException。)
  - 语义。有时,一定要记住这一点:两个引用指向的是同一个对象而不是两个相同的对象。
  - 有关这些原因,一些 DataContractSerializer 构造函数重载具有 preserveObjectReferences 参数(默认值为false)。在将次参数设置为true时,将使用只有WCF才可以理解的编码引用的特殊方法。当设置为true是,XML 代码示例现在如下所示:
    <PurchaseOrder ser:id="1">
      <billTo ser:id="2"><street ser:id="3">123 Main st.</street></billTo>
      <shipTo ser:ref="2"/>
    </PurchaseOrder>
- "ser"命名空间引用标准序列化命名空间
  http://schemas.microsoft.com/2003/10/Serialization/。每一段数据只进行一次序列化并获得一个ID号,后续使用会导致引用已序列化的数据。
- 此模式的限制
  - DataContractSerializer 在 preserveObjectReferences 设置为 true 的情况下生成的 XML 与任何其他技术都无法进行交互,仅可以由另一个其 preserveObjectReferences 也设置为true的 DataContractSerializer 实例进行访问。
  - 元数据(架构)不支持此功能。生成的架构仅对 preserveObjectReferences 设置为false 的情况有效。
  - 此功能肯能导致序列化和反序列化进程运行速度减慢。尽管不必复制数据,但是在此模式中必须执行额外的对象比较。


序列化
- 简单序列化
  - 对对象进行序列化最基本的方法是将其传递到 WriteObject 方法。
  - 该方法有三个重载,每个重载分布用于写入到 Stream、XmlWriter 或 XmlDictionaryWriter 。
  - 使用 XmlDictionaryWriter 重载时,序列化程序会针对二进制 XML 优化输出。
    Person p = new Person();
    DataContractSerializer dcs = new DataContractSerializer(typeof(Person));
    XmlDictionaryWriter xdw = XmlDictionaryWriter.CreateTextWriter(someStream,Encoding.UTF8);
    <Person>
      <Name>Jay Hamlin</Name>
      <Address>123 Main st.></Address>
    </Person>
- 分步引导的序列化
  - WriteStartObject 、 WriteObjectContent 和 WriteEndObject 方法可分布用于写入结束元素、写入对象内容以及关闭包装元素
  - 此分步引导的序列化具有两个常见用途。一种用途是在 WriteStartObject 和 WriteObjectContent 之间插入内容(例如属性或注释),如下所示:
  Person p = new Person();
  DataContractSerializer dsc = new DataContractSerializer(typeof(Person));
  XmlDictionaryWriter xdw = XmlDictionaryWriter.CreateTextWriter(someStream,Encoding.UTF8);
  dsc.WriteStartObject(xdw,p);
  xdw.WriteAttributeString("serializedBy","myCode");
  dcs.WriteObjectContent(xdw,p);
  dcs.WriteEndObject(xdw);

  <Person serializedBy="myCode">
    <Name>Jay Hamlin</Name>
    <Address>123 Main st.</Address>
  </Person>
- 另一种常见用途是完全避免使用 WriteStartObject 和 WriteEndObject,并写入自己的自定义包装元素(),如下代码中所示
  Person p = new Person();
  DataContractSerializer dsc = new DataContractSerializer(typeof(Person));
  XmlDictionaryWriter xdw = XmlDictionaryWriter.CreateTextWriter(someStream,Encoding.UTF8);
  xdw.WriteStartObject("MyCustomWrapper"); 
  dcs.WriteObjectContent(xdw,p);
  dcs.WriteEndObject();

  <MyCustomWrapper>
    <Name>Jay Hamlin</Name>
    <Address>123 Main st.</Address>
  </MyCustomWrapper>


反序列化
- 对对象进行反序列化的最基本的方式是调用 ReadObject 方法重载之一。
  - 该方法有三个重载,每个重载分别用于读取 XmlDictionaryReader 、 XmlReader 或 Stream 。请注意, Stream 重载将创建不受任何配额保护的文本 XmlDictionaryReader ,此重载仅引用于读取受信任的数据。
  - 还请注意,必须将 ReadObject 方法返回的对象强制转换为适当的类型
  DataContractSerializer dsc = new DataContractSerializer(typeof(Person));
  FileStream fs = new FileStream(path,FileMode.Open);
  XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(fs,new XmlDictionaryReaderQuotas());

  Person p = (Person)dcs.ReaderObject(reader);

XmlSerializer
- WCF 还支持 XmlSerializer 类
- XmlSerializer 类并非专用于 WCF, ASP.NET Web 服务同样也使用该序列化引擎。
- XmlSerializer 类支持的类型少于 DataContractSerializer 类支持的类型,但他允许对生成的 XML 进行更多的控制,并且支持更多的 XML 架构定义语言(XSD)标准。
- 它也不需要在可序列化类型上有任何声明性属性。
- XmlSerializer 类并不支持数据协定类型。
- 手动切换到 XmlSerializer
  - 将应用程序从 ASP.NET Web 服务迁移到 WCF 时,您可能需要重用现有的、与 XmlSerializer 兼容的类型,而不是创建新的数据协定类型。
  - 当对出现在消息中的 XML 的精确控制很重要,而 Web 服务描述语言 (WSDL) 文档不可用时,例如,在使用必须遵循某个已标准化且已发布的架构(与 DataContractSerializer 不兼容)的类型来创建服务时。
  - 创建遵循旧式 SOAP 编码标准的服务时。
  [ServiceContract]
  [XmlSerializerFormat]
  public class BankingService
  {
    [OperationContract]
    public void ProcessTransacion(BankingTransaction bt)
    {
      // code not shown.
    }
  }
  // BankingTransaction is not a data contract class,
  // but is an XmlSerializer-compatible class instead.
  public class BankingTransaction
  {
    [XmlAttribute]
    public string Operation;
    [XmlElement]
    public Account fromAccount;
    [XmlElement]
    public Account toAccount;
    [XmlElement]
    public int amount;
  }
  // Notice that the account class must also be  XmlSerializer-compatible
- 在序列化数据协定类型时,DataContractSerializer 类只序列化 DataMemberAttribute 属性标记的成员。 XmlSerializer 类序列化任何公共成员
  [DataContract]
  public class Customer
  {
    [DataMember]
    public string firstName;
    [DataMember]
    public string lastName
    public string creditCardNumber;
  }
  如果在选择了 XmlSerializer 类的服务协定中不慎使用了该类型,则将序列化 creditCardNumber 成员,这肯能并不是想要的结果。

附件1
- DataContractSerizlizer支持的类型
  - 数据协定类型。这些是已应用了 DataContractAttribute 属性的类型。表示业务对象的新自定义类型通常应作为数据协定类型创建。有关更多信息,请参见使用数据协定。
  - 集合类型。这些是表示数据列表的类型。这些类型可以使常规的类型数组或集合类型,例如 ArrayList 和 Dictionary。
  CollectionDataContractAttribute 属性可以用于自定义这些类型的序列化,但不是必需的。有关更多的信息,请参见数据协定中的集合类型。
  - 枚举类型。枚举(包括标志枚举)是可序列化的。或者,可以使用 DataContractAttribute 属性对枚举类型进行标记,在这种情况下,必须使用 EnumMemberAttribute 属性对每个成员进行标记。有关更多信息,请参见数据协定中的枚举类型。
  - .NET Framework 基元类型。集成到 .NET Framework 中的下列类型都可以进行序列化,并可视为基元类型:Byte,SByte,Int16,Int31,Int64,UInt16,UInt32,UInt64,Single,Double,Boolean,Char,Decimal,Object 和 String 。

附件2
- 其他基元类型。这些类型不是 .NET Framework 中的基元,但可作为采用序列化的 XML 形式的基元。 这些类型有 DateTime,DataTimeOffset,TimSpan,Guid,Uri,XmlQualifiledName 和 Byte 数
- 使用 SerializableAttribute 属性标记的类型。 .NET Framework 基类库中包含的许多类型都属于此类别。DataContractSerizlizer 完全支持由 .NET Framework 远程处理、 BinaryFormatter 和 SoapFormatter 使用的此序列化编程模型,包括支持 ISerializable 接口。
- 表示原始 XML 的类型或表示 ADO.NET 关系数据的类型。 支持 XmlElement 和 XmlNode 类型的数组作为一种直接表示 XML 的方式。另外,支持实现 IXmlSerializable 接口的类型,包括相关的 XmlSchemalProviderAttribute 属性、 XDocument 和 XElement 类型。 ADO.NET DataTble 类型和 DataSet 类型(以及其类型化的派生类)都实现 IXmlSerializable 接口,因此可归入此类别。
 

原文地址:https://www.cnblogs.com/htht66/p/2328857.html