WCF下的序列化与反序列化解决的是数据在两种状态之间的相互转化:托管类型对象和XML。由于类型定义了对象的数据结构,所以无论对于序列化还是反序列化,都必须事先确定对象的类型。如果被序列化对象或者被反序列化生成的对象包含不可知的类型,序列化或者反序列化将会失败。为了确保DataContractSerializer的正常序列化和反序列化,我们需要将“未知”类型加入DataContractSerializer“已知”类型列表中。

一、未知类型导致序列化失败

.NET的类型可以分为两种:声明类型和真实类型。我们提倡面向接口的编程,对象的真实类型往往需要在运行时才能确定,在编程的时候往往只需要指明类型的声明类型,比如类型实现的接口或者抽象类。当我们使用基于接口或者抽象类创建的DataContractSerializer去序列化一个实现了该接口或者继承该抽象类的实例的时候,往往会因为对对象的真实类型无法识别造成不能正常地序列化。比如下面的代码中,我们定义了3个类型,一个接口、一个抽象类和一个具体类。

 1: namespace Artech.DataContractSerializerDemos

 2: {

 3: public interface IOrder

 4: {

 5: Guid ID

 6: { get; set; }

 7:  

 8: DateTime Date

 9: { get; set; }

 10:  

 11: string Customer

 12: { get; set; }

 13:  

 14: string ShipAddress

 15: { get; set; }

 16: }

 17:  

 18: [DataContract]

 19: public abstract class OrderBase : IOrder

 20: {

 21: [DataMember]

 22: public Guid ID

 23: { get; set; }

 24:  

 25: [DataMember]

 26: public DateTime Date

 27: { get; set; }

 28:  

 29: [DataMember]

 30: public string Customer

 31: { get; set; }

 32:  

 33: [DataMember]

 34: public string ShipAddress

 35: { get; set; }

 36: }

 37:  

 38: [DataContract]

 39: public class Order : OrderBase

 40: {

 41: [DataMember]

 42: public double TotalPrice

 43: { get; set; }

 44: }

 45: }

当我们通过下面的方式去序列化一个Order对象(注意泛型类型为IOrder或者OrderBase),将会抛出如图1所示SerializationException异常,提示Order类型无法识别。

注:Serialize<T>方法的定义,请参考本系列的上篇文章:《WCF技术剖析之十二:数据契约(Data Contract)和数据契约序列化器(DataContractSerializer)》。

 1: Order order = new Order()

 2: {

 3: ID = Guid.NewGuid(),

 4: Customer = "NCS",

 5: Date = DateTime.Today,

 6: ShipAddress = "#328, Airport Rd, Industrial Park, Suzhou Jiangsu Province",

 7: TotalPrice = 8888.88

 8: };

 9:  

 10: Serialize<IOrder>(order, @"E:\order.xml");

 11: //或者

 12: Serialize<OrderBase>(order, @"E:\order.xml");

图1 “未知”类型导致的序列化异常

二、DataContractSerializer的已知类型集合

解决上面这个问题的唯一途径就是让DataContractSerializer能够识别Order类型,成为DataContractSerializer的已知类型(Known Type)。DataContractSerializer内部具有一个已知类型的列表,我们只需要将Order的类型添加到这个列表中,就能从根本上解决这个问题。通过下面6个重载构造函数中的任意一个,均可以通过knownTypes参数指定DataContractSerializer的已知类型集合,该集合最终反映在DataContractSerializer的制度属性KnownTypes上。

 1: public sealed class DataContractSerializer : XmlObjectSerializer

 2: {

 3: public DataContractSerializer(Type type, IEnumerable<Type> knownTypes);

 4: public DataContractSerializer(Type type, string rootName, string rootNamespace, IEnumerable<Type> knownTypes);

 5: public DataContractSerializer(Type type, XmlDictionaryString rootName, XmlDictionaryString rootNamespace, IEnumerable<Type> knownTypes);

 6: public DataContractSerializer(Type type, IEnumerable<Type> knownTypes, int maxItemsInObjectGraph, bool ignoreExtensionDataObject, bool preserveObjectReferences, IDataContractSurrogate dataContractSurrogate);

 7: public DataContractSerializer(Type type, string rootName, string rootNamespace, IEnumerable<Type> knownTypes, int maxItemsInObjectGraph, bool ignoreExtensionDataObject, bool preserveObjectReferences, IDataContractSurrogate dataContractSurrogate);

 8: public DataContractSerializer(Type type, XmlDictionaryString rootName, XmlDictionaryString rootNamespace, IEnumerable<Type> knownTypes, int maxItemsInObjectGraph, bool ignoreExtensionDataObject, bool preserveObjectReferences, IDataContractSurrogate dataContractSurrogate);

 9: 

 10: public ReadOnlyCollection<Type> KnownTypes { get; }

 11: }

为了方便后面的演示,我们对我们使用的泛型服务方法Serialize<T>为已知类型作相应的修正,通过第3个参数指定DataContractSerializer的已知类型列表。

 1: public static void Serialize<T>(T instance, string fileName, IList<Type> konwnTypes)

 2: {

 3: DataContractSerializer serializer = new DataContractSerializer(typeof(T), konwnTypes, int.MaxValue, false, false, null);

 4: using (XmlWriter writer = new XmlTextWriter(fileName, Encoding.UTF8))

 5: {

 6: serializer.WriteObject(writer, instance);

 7: }

 8: Process.Start(fileName);

 9: }

三、基于接口的序列化

DataContractSerializer的创建必须基于某个确定的类型,这里的类型既可以是接口,也可以是抽象类或具体类。不过基于接口的DataContractSerializer与基于抽象数据契约类型的DataContractSerializer,在进行序列化时表现出来的行为是不相同的。

在下面的代码中,在调用Serialize<T>的时候,将泛型类型分别设定为接口IOrder和抽象类OrderBase。虽然是对同一个Order对象进行序列化,但是序列化生成的XML却各有不同。文件order.interface.xml的根节点为<z:anyType>,这是因为DataContractAttribute不能应用于接口上面,所以接口不具有数据契约的概念。<z:anyType>表明能够匹配任意类型,相当于类型object。

 1: Order order = new Order()

 2: {

 3: ID = Guid.NewGuid(),

 4: Customer = "NCS",

 5: Date = DateTime.Today,

 6: ShipAddress = "#328, Airport Rd, Industrial Park, Suzhou Jiangsu Province",

 7: TotalPrice = 8888.88

 8: };

 9:  

 10: Serialize<IOrder>(order, @"E:\order.interface.xml", new List<Type>{typeof(Order)});

 11: Serialize<OrderBase>(order, @"E:\order.class.xml", new List<Type> { typeof(Order) });

 1: <z:anyType xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:d1p1="http://schemas.datacontract.org/2004/07/Artech.DataContractSerializerDemos" i:type="d1p1:Order" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">

 2: <d1p1:Customer>NCS</d1p1:Customer>

 3: <d1p1:Date>2008-12-04T00:00:00+08:00</d1p1:Date>

 4: <d1p1:ID>04c07e41-6302-48d1-ac06-87ebbff2b75f</d1p1:ID>

 5: <d1p1:ShipAddress>#328, Airport Rd, Industrial Park, Suzhou Jiangsu Province</d1p1:ShipAddress>

 6: <d1p1:TotalPrice>8888.88</d1p1:TotalPrice>

 7: </z:anyType>

 1: <OrderBase xmlns:i="http://www.w3.org/2001/XMLSchema-instance" i:type="Order" xmlns="http://schemas.datacontract.org/2004/07/Artech.DataContractSerializerDemos">

 2: <Customer>NCS</Customer>

 3: <Date>2008-12-04T00:00:00+08:00</Date>

 4: <ID>04c07e41-6302-48d1-ac06-87ebbff2b75f</ID>

 5: <ShipAddress>#328, Airport Rd, Industrial Park, Suzhou Jiangsu Province</ShipAddress>

 6: <TotalPrice>8888.88</TotalPrice>

 7: </OrderBase>

实际上,在WCF应用中,如果服务契约的操作的参数定义为接口,在发布出来的元数据中,接口类型就相当于object,并且当客户端通过添加服务引用生成客户端服务契约的时候,相应的参数类型就是object类型。比如对于下面的服务契约的定义,当客户端导出后将变成后面的样式。

 1: [ServiceContract(Namespace="http://www.artech.com/")]

 2: public interface IOrderManager

 3: {

 4: [OperationContract]

 5: void ProcessOrder(IOrder order);

 6: }

 1: [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]

 2: [System.ServiceModel.ServiceContractAttribute(ConfigurationName = "ServiceReferences.IOrderManager")]

 3: public interface IOrderManager

 4: {

 5:  

 6: [System.ServiceModel.OperationContractAttribute(Action = "http://www.artech.com/IOrderManager/ProcessOrder", ReplyAction = "http://www.artech.com/IOrderManager/ProcessOrderResponse")]

 7: void ProcessOrder(object order);

 8: }

四、 KnownTypeAttribute与ServiceKnownTypeAttribute

对于已知类型,可以通过两个特殊的自定义特性进行设置:KnownTypeAttribute和ServiceKnownTypeAttribute。KnownTypeAttribute应用于数据契约中,用于设置继承与该数据契约类型的子数据契约类型,或者引用的其他潜在的类型。ServiceKnownTypeAttribute既可以应用于服务契约的接口和方法上,也可以应用在服务实现的类和方法上。应用的目标元素决定了定义的已知类型的作用范围。下面的代码中,在基类OrderBase指定了子类的类型Order。

 1: [DataContract]

 2: [KnownType(typeof(Order))]

 3: public abstract class OrderBase : IOrder

 4: {

 5: //省略成员

 6: }

而ServiceKnownTypeAttribute特性,仅可以使用在服务契约类型上,也可以应用在服务契约的操作方法上。如果应用在服务契约类型上,已知类型在所有实现了该契约的服务操作中有效,如果应用于服务契约的操作方法上,则定义的已知类型在所有实现了该契约的服务对应的操作中有效。

 1: [ServiceContract]

 2: [ServiceKnownType(typeof(Order))]

 3: public interface IOrderManager

 4: {

 5: [OperationContract]

 6: void ProcessOrder(OrderBase order);

 7: }

 1: [ServiceContract]

 2: public interface IOrderManager

 3: {

 4: [OperationContract]

 5: [ServiceKnownType(typeof(Order))]

 6: void ProcessOrder(OrderBase order);

 7: }

ServiceKnownTypeAttribute也可以应用于具体的服务类型和方法上面。对于前者,通过ServiceKnownTypeAttribute定义的已知类型在整个服务的所有方法中有效,而对于后者,则已知类型仅限于当前方法。

 1: [ServiceKnownType(typeof(Order))]

 2: public class OrderManagerService : IOrderManager

 3: { 

 4: public void ProcessOrder(OrderBase order)

 5: {

 6: //省略成员

 7: }

 8: }

 1: public class OrderManagerService : IOrderManager

 2: {

 3: [ServiceKnownType(typeof(Order))]

 4: public void ProcessOrder(OrderBase order)

 5: {

 6: //省略成员

 7: }

 8: }

除了通过自定义特性的方式设置已知类型外,已知类型还可以通过配置的方式进行指定。已知类型定义在<system.runtime.serialization>配置节中,采用如下的定义方式。这和我们在上面通过KnownTypeAttribute指定Order类型是完全等效的。

 1: <?xml version="1.0" encoding="utf-8" ?>

 2: <configuration>

 3: <system.runtime.serialization>

 4: <dataContractSerializer>

 5: <declaredTypes>

 6: <add type="Artech.DataContractSerializerDemos.OrderBase,Artech.DataContractSerializerDemos.KnownTypes">

 7: <knownType type="Artech.DataContractSerializerDemos.Order,Artech.DataContractSerializerDemos.KnownTypes"/>

 8: </add>

 9: </declaredTypes>

 10: </dataContractSerializer>

 11: </system.runtime.serialization>

 12: </configuration>

 

C# 序列化过程中的已知类型(Known Type)的更多相关文章

  1. WCF技术剖析之十三:序列化过程中的已知类型(Known Type)

    原文:WCF技术剖析之十三:序列化过程中的已知类型(Known Type) [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经>为此录制的节目视频(苏州话) ...

  2. wcf已知类型 known type

    .服务契约的定义 /* Copyright (c) 2014 HaiHui Software Co., Ltd. All rights reserved * * Create by huanglc@h ...

  3. WCF 已知类型和泛型解析程序 KnownType

    数据协定继承 已知类型和泛型解析程序 Juval Lowy 下载代码示例 自首次发布以来,Windows Communication Foundation (WCF) 开发人员便必须处理数据协定继承方 ...

  4. 项目文件中的已知 NuGet 属性(使用这些属性,创建 NuGet 包就可以不需要 nuspec 文件啦)

    知道了 csproj 文件中的一些常用 NuGet 属性,创建 NuGet 包时就可以充分发挥新 Sdk 自动生成 NuGet 包的优势,不需要 nuspec 文件啦.(毕竟 nuspec 文件没有 ...

  5. WCF数据契约代理和已知类型的使用

    using Bll; using System; using System.CodeDom; using System.Collections.Generic; using System.Collec ...

  6. WCF 之 已知类型(KnownType)

    已知类型(Known types)允许在服务契约中使用多态的行为,在服务操作中暴露基本类型.将已知类型(known types)相关到基本类型(基类类型)自身;特定操作;整个服务契约采用属性声明或者配 ...

  7. java基础 File与递归练习 使用文件过滤器筛选将指定文件夹下的小于200K的小文件获取并打印按层次打印(包括所有子文件夹的文件) 多层文件夹情况统计文件和文件夹的数量 统计已知类型的数量 未知类型的数量

    package com.swift.kuozhan; import java.io.File; import java.io.FileFilter; /*使用文件过滤器筛选将指定文件夹下的小于200K ...

  8. 在Excel中,已知身份证号码,如何批量计算其实际年龄?

    昨天,上司问我:..,你会在Excel中计算年龄吗?当时,一下促住了.说真的,还真不会.今天研究了一下,写下来,方便日后查看. 首先,得有一张已知姓名和相应身份证号的原表吧. 在这张表上再加上三列:出 ...

  9. WCF中数据契约之已知类型的几种公开方式

    WCF中传输的数据不想传统的面向对象编程,它只传递了一些对象的属性,但是自身并不知道自己属于什么对象,所以,他没有子类和父类的概念,因而也就没有Is-a的关系,所以在WCF中,如果想维持这种继承关系, ...

随机推荐

  1. 用keil直接生成BIN文件

    1.下载hex2bin到keil的任意目录,下载地址:http://www.keil.com/download/docs/7.asp 或者 http://www.hex2bin.com/files/h ...

  2. sp_xml_preparedocument _使用 处理XML文档

      有时会在存储过程中处理一些XML格式的数据,所以会用到sp_xml_preparedocument,他可以将XML数据进行读取,然后使用 MSXML 分析器 (Msxmlsql.dll) 对其进行 ...

  3. java内存模型和线程

    概述 多任务的处理在现在的计算机中可以说是"标配"了,在许多的情况下,让计算机同时做几件事情,不仅是因为计算机的运算能力的强大,还有一个重要的原因是:cpu的运算速度和计算机的存储 ...

  4. jar包版本冲突,并且要保留两个版本都能使用

    问题:在做项目时,遇到jar版本冲突的问题,并且老代码依赖不能用新jar包代替,要保证功能不变须要保证两个jar都能使用 思路:使用runtime 的exec 方式另启线程运行,然后返回结果 解决: ...

  5. Java集合的实现细节—Set集合和Map集合

    Set:代表无序.不可重复的集合 Map:代表key-value对集合,也称为关联数组 从表面上看,Set和Map相似性很少,但实际上可以说Map集合时Set集合的扩展. 1.Set集合和Map集合的 ...

  6. iOS键盘遮挡输入框,输入区域自动上移

    在iOS开发过程当中,遇到关于键盘遮挡输入框的问题,经过网络参考与实践,总结如下: 登录窗口,上下放置两个UITextField,一个用户名,一个密码,放置的在屏幕下方1/3处,当点击用户名时,自动弹 ...

  7. iOS UILabel UITextView UIButton 等等显示文本行间距

    iOS UILabel  UITextView UIButton 等等显示文本行间距都用如下方法 NSMutableParagraphStyle *paragraphStyle = [[NSMutab ...

  8. 站点建设10个最好的响应的HTML5滑块插件

    大多数的最佳响应的HTML5滑块插件能够使用移动应用程序,站点建设项目,以及Web开发项目提供一些令人兴奋的功能,如无限的动画效果,百分之中的一个百响应布局设计和很多其它. 1.别急!慢慢来 功能丰富 ...

  9. CFGYM 2013-2014 CT S01E03 D题 费用流模版题

    题意: n行, a房间的气球,b房间的气球 i行需要的气球,与a房的距离,b房的距离 求最小距离 #include <stdio.h> #include <string.h> ...

  10. Android-它们的定义Dialog

    Android-它们的定义Dialog 2014年4月27日 星期天 天气晴朗 心情平静 本篇博文来分享一个也是开发中常常须要用到的功能-自己定义对话框,这里我用到了Android中的图形资源shap ...