博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
WCF 序列化与反序列化复杂类型(DataContractSerializer)
阅读量:5731 次
发布时间:2019-06-18

本文共 18982 字,大约阅读时间需要 63 分钟。

作者: 出处:

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

 现在,我们定义两个带数据协定的类——ActionInfo和ActionParameterInfo:

///     /// 动作信息    ///     [DataContract(Namespace = "http://blog.csdn.net/jiankunking")]    [KnownType("GetKnowTypes")]    //[KnownType("GetKnowTypesQuote")]    public class ActionInfo    {        private string actionName;        ///         /// 动作名称         ///         [DataMember]        public String ActionName        {            get { return actionName; }            set { actionName = value; }        }        private string actionId;        ///         /// 动作唯一标识        ///         [DataMember]        public String ActionId        {            get { return actionId; }            set { actionId = value; }        }        private Dictionary
actionParameters; ///
/// 参数信息 /// [DataMember] public Dictionary
ActionParameters { get { if (actionParameters == null) { actionParameters = new Dictionary
(); } return actionParameters; } set { actionParameters = value; } } static Type[] GetKnowTypes() { return new Type[] { typeof(Dictionary
) }; } //[DataMember] //public object Quote; //static Type[] GetKnowTypesQuote() //{ // return new Type[] { typeof(Dictionary
) }; //} } ///
/// 动作参数 /// //[DataContract(Namespace = "http://blog.csdn.net/jiankunking")] [DataContract] public class ActionParameterInfo { private bool parameterAllowDBNull; ///
/// 参数是否允许为空 /// //[DataMember(Name = "参数是否允许为空", Order = 2)] [DataMember] public bool ParameterAllowDBNull { get { return parameterAllowDBNull; } set { parameterAllowDBNull = value; } } private ActionInfoParameterCategoryEnum parameterCategory; ///
/// 参数分类 /// //[DataMember(Name = "参数分类", Order = 3)] [DataMember] public ActionInfoParameterCategoryEnum ParameterCategory { get { return parameterCategory; } set { parameterCategory = value; } } private object parameterValue; ///
/// 参数值 /// //[DataMember(Name = "参数值", Order = 1)] [DataMember] public object ParameterValue { get { return parameterValue; } set { parameterValue = value; } } private string parameterCode; ///
/// 参数编号(未添加DataMember标识) /// public string ParameterCode { get { return parameterCode; } set { parameterCode = value; } } public enum ActionInfoParameterCategoryEnum { [Description("普通类型")] CommonType = 0, [Description("事件源")] EventSource = 1, [Description("事件参数")] EventArgument = 2, [Description("控件ID")] ControlId = 3, [Description("表单ID")] FormId = 4 }

在代码中添加以下代码填充实体类:

ActionInfo ac = new ActionInfo();            ac.ActionId = "ActionId" + Guid.NewGuid().ToString();            ac.ActionName = "ActionName" + Guid.NewGuid().ToString();            ac.ActionParameters = new Dictionary
(); for (int i = 0; i < 2; i++) { ActionParameterInfo ap = new ActionParameterInfo(); ap.ParameterAllowDBNull = false; ap.ParameterCategory = ActionInfoParameterCategoryEnum.CommonType; ap.ParameterValue = Guid.NewGuid(); ac.ActionParameters.Add(ap.ParameterValue.ToString(), ap); }
      测试序列化:

string ss = PubXmlSerializer
.ToXMLString(ac);
生成的xml如下:

ActionIdb9da9159-068e-4cae-9810-5e2ca4f0a87f
ActionName70737f3b-1672-437e-8ad5-543038ac62b4
4e059222-023f-40aa-b08e-42e377570092
false
CommonType
4e059222-023f-40aa-b08e-42e377570092
5c528920-a7c6-4fc4-8632-a2f083d43676
false
CommonType
5c528920-a7c6-4fc4-8632-a2f083d43676

  从xml看到:

DataContractSerializer在默认的情况下采用了如下的序列化规则。

1、XML的根节点名称为数据契约类型的名称。
2、默认的命名空间采用的格式为  http://schemas.datacontract.org/2004/07/DataContractSerializerDemo (数据契约类型的命名空间)。
3、只有显示地应用了DataMemberAttributue特性的字段或属性才能作为数据成员参与序列化。
4、所有数据成员均以XML元素的形式被序列化。
5、序列化后数据成员在XML中的次序采用这样的规则:父类数据成员在前,子类数据成员在后;定义在同一个类型中的数据成员按照字母排序。

如果默认序列化后的xml结构不能满足我们的需求,则可以通过DataContractAttribute和DataMenmberAttribute这两个特性对其进行修正。在下面我们通过DataContractAttribute特性设置了数据契约的名称和命名空间,通过DataMenmberAttribute特性的Name属性为Name和Birthday两个属性设置不同于属性名称的数据成员名称,并通过Order控制数据成员的先后次序。

   修改ActionParameterInfo类:

///     /// 动作参数    ///     [DataContract(Namespace = "http://blog.csdn.net/jiankunking")]    public class ActionParameterInfo    {        private bool parameterAllowDBNull;        ///         /// 参数是否允许为空        ///         [DataMember(Name = "参数是否允许为空", Order = 2)]        public bool ParameterAllowDBNull        {            get { return parameterAllowDBNull; }            set { parameterAllowDBNull = value; }        }        private ActionInfoParameterCategoryEnum parameterCategory;        ///         /// 参数分类        ///         [DataMember(Name = "参数分类", Order = 3)]        public ActionInfoParameterCategoryEnum ParameterCategory        {            get { return parameterCategory; }            set { parameterCategory = value; }        }        private object parameterValue;        ///         /// 参数值        ///         [DataMember(Name = "参数值", Order = 1)]        public object ParameterValue        {            get { return parameterValue; }            set { parameterValue = value; }        }        private string parameterCode;        ///         /// 参数编号(未添加DataMember标识)        ///         public string ParameterCode        {            get { return parameterCode; }            set { parameterCode = value; }        }
再次序列化,xml结构如下:

ActionIdd95e434d-4f13-49b7-859e-a7964f468d70
ActionNamef750900a-42fe-4586-9688-768cdefd4d78
560e9e05-2c7d-482a-89f5-ea200bbe42b3
<参数值 i:type="b:guid" xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/">
560e9e05-2c7d-482a-89f5-ea200bbe42b3
<参数是否允许为空>
false
<参数分类>
CommonType
eac9bc48-be0c-4c36-92d0-8f392f010516
<参数值 i:type="b:guid" xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/">
eac9bc48-be0c-4c36-92d0-8f392f010516
<参数是否允许为空>
false
<参数分类>
CommonType
有木有发现DataMember中Name与Order的作用啊
大笑

        小注:

对于一些比较复杂的类型无法序列化、反序列化(不能识别类型)的时候,就得考虑使用KnownTypeAttribute来标注可能涉及到的外部类型,但如果遇到像泛型这些较为复杂的类型,就要考虑在带数据协定的类中添加一个静态方法,该方法返回Type 的IEnumerable,一般是Type[]就可以了,而在KnownTypeAttribute的构造函数中使用这个方法的名字。

在该demo中将ActionInfo修改为如下形式(即去掉static Type[] GetKnowTypes()函数,直接标识属性类型)也是可以正常序列化反序列化的:

///     /// 动作信息    ///     [DataContract(Namespace = "http://blog.csdn.net/jiankunking")]    //[KnownType("GetKnowTypes")]    //[KnownType("GetKnowTypesQuote")    [KnownType(typeof(Dictionary
))] public class ActionInfo { private string actionName; ///
/// 动作名称 /// [DataMember] public String ActionName { get { return actionName; } set { actionName = value; } } private string actionId; ///
/// 动作唯一标识 /// [DataMember] public String ActionId { get { return actionId; } set { actionId = value; } } private Dictionary
actionParameters; ///
/// 参数信息 /// [DataMember] public Dictionary
ActionParameters { get { if (actionParameters == null) { actionParameters = new Dictionary
(); } return actionParameters; } set { actionParameters = value; } } //static Type[] GetKnowTypes() //{ // return new Type[] { typeof(Dictionary
) }; //} //[DataMember] //public object Quote; //static Type[] GetKnowTypesQuote() //{ // return new Type[] { typeof(Dictionary
) }; //} }
修改实体类后的序列化xml如下:

ActionId394e78b1-0cad-4726-9d6d-6061b7082571
ActionName05ddd5d3-4c08-4bf9-ae7d-2166625dee94
ebb9c44f-69c8-4a52-b59d-7cd8da066591
<参数值 i:type="b:guid" xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/">
ebb9c44f-69c8-4a52-b59d-7cd8da066591
<参数是否允许为空>
false
<参数分类>
CommonType
1a9f70f8-cf38-4644-a466-6afba99b8837
<参数值 i:type="b:guid" xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/">
1a9f70f8-cf38-4644-a466-6afba99b8837
<参数是否允许为空>
false
<参数分类>
CommonType

  那可不可以去掉KnownType标识呢?答案是不可以的。

那么在ActionInfo类中是否可以定义两个已知类型呢?

经过测试验证,不能再同一个类中定义两个已知类型,比如:

同时在类上标识:

错误信息如下:

------------------------------------------------------System.Runtime.Serialization.InvalidDataContractException: 类型“SerializationDemo.ActionInfo”的 KnownTypeAttribute 特性指定一个名为“GetKnowTypes”的方法,以提供已知类型。此类型上找不到静态方法“GetKnowTypes()”。请确保此方法存在并标记为静态。   在 System.Runtime.Serialization.DataContract.DataContractCriticalHelper.ThrowInvalidDataContractException(String message, Type type)   在 System.Runtime.Serialization.DataContract.ImportKnownTypeAttributes(Type type, Dictionary`2 typesChecked, Dictionary`2& knownDataContracts)   在 System.Runtime.Serialization.DataContract.ImportKnownTypeAttributes(Type type)   在 System.Runtime.Serialization.ClassDataContract.ClassDataContractCriticalHelper.get_KnownDataContracts()   在 System.Runtime.Serialization.ClassDataContract.get_KnownDataContracts()   在 System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithoutXsiType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle)   在 System.Runtime.Serialization.Json.DataContractJsonSerializer.InternalWriteObjectContent(XmlWriterDelegator writer, Object graph)   在 System.Runtime.Serialization.Json.DataContractJsonSerializer.InternalWriteObject(XmlWriterDelegator writer, Object graph)   在 System.Runtime.Serialization.XmlObjectSerializer.InternalWriteObject(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver)   在 System.Runtime.Serialization.XmlObjectSerializer.WriteObjectHandleExceptions(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver)   在 System.Runtime.Serialization.Json.DataContractJsonSerializer.WriteObject(XmlDictionaryWriter writer, Object graph)   在 System.Runtime.Serialization.Json.DataContractJsonSerializer.WriteObject(Stream stream, Object graph)   在 SerializationDemo.JsonSerializer.JsonStringSerializer[T](T t) 位置 E:\WorkSpace\WorkSpaceTest\SerializationDemo\SerializationDemo\JsonSerializer.cs:行号 29---------------------------确定   ---------------------------
但是对于同一个类型,在一个类中可以有多个属性,比如:

   

填充实体类代码如下:

ActionInfo ac = new ActionInfo();            ac.ActionId = "ActionId" + Guid.NewGuid().ToString();            ac.ActionName = "ActionName" + Guid.NewGuid().ToString();            ac.ActionParameters = new Dictionary
(); for (int i = 0; i < 2; i++) { ActionParameterInfo ap = new ActionParameterInfo(); ap.ParameterAllowDBNull = false; ap.ParameterCategory = ActionInfoParameterCategoryEnum.CommonType; ap.ParameterValue = Guid.NewGuid(); ac.ActionParameters.Add(ap.ParameterValue.ToString(), ap); } ac.ActionParameters1 = new Dictionary
(); for (int i = 0; i < 2; i++) { ActionParameterInfo ap = new ActionParameterInfo(); ap.ParameterAllowDBNull = false; ap.ParameterCategory = ActionInfoParameterCategoryEnum.CommonType; ap.ParameterValue = Guid.NewGuid(); ac.ActionParameters1.Add(ap.ParameterValue.ToString(), ap); }
序列化之后的xml如下:

ActionIded35fdb5-0c47-440e-9d6e-8dba364d49d5
ActionNamef991e495-7cc2-43a2-863f-4a9c139641b1
aec5004a-79d3-4949-8c70-eb7605aa6702
<参数值 i:type="b:guid" xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/">
aec5004a-79d3-4949-8c70-eb7605aa6702
<参数是否允许为空>
false
<参数分类>
CommonType
d859df4f-1b82-4144-9f1b-3a4c01ff1a10
<参数值 i:type="b:guid" xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/">
d859df4f-1b82-4144-9f1b-3a4c01ff1a10
<参数是否允许为空>
false
<参数分类>
CommonType
110478a6-a177-4dec-bc7f-77027bd30bd3
<参数值 i:type="b:guid" xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/">
110478a6-a177-4dec-bc7f-77027bd30bd3
<参数是否允许为空>
false
<参数分类>
CommonType
cdf8d367-dac4-4ae0-a6b7-d3de0f9fc92a
<参数值 i:type="b:guid" xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/">
cdf8d367-dac4-4ae0-a6b7-d3de0f9fc92a
<参数是否允许为空>
false
<参数分类>
CommonType

如果就是有多个复杂属性如何处理呢?

铛铛==》

[KnownType(typeof(Dictionary
))] [KnownType(typeof(Dictionary
))]

        修改ActionInfo类为:

///     /// 动作信息    ///     [DataContract(Namespace = "http://blog.csdn.net/jiankunking")]    //[KnownType("GetKnowTypes")]    //[KnownType("GetKnowTypesQuote")]    //[KnownType(typeof(Dictionary
))] [KnownType(typeof(Dictionary
))] [KnownType(typeof(Dictionary
))] public class ActionInfo { private string actionName; ///
/// 动作名称 /// [DataMember] public String ActionName { get { return actionName; } set { actionName = value; } } private string actionId; ///
/// 动作唯一标识 /// [DataMember] public String ActionId { get { return actionId; } set { actionId = value; } } private Dictionary
actionParameters; ///
/// 参数信息 /// [DataMember] public Dictionary
ActionParameters { get { if (actionParameters == null) { actionParameters = new Dictionary
(); } return actionParameters; } set { actionParameters = value; } } //private Dictionary
actionParameters1; /
/ 参数信息 / //[DataMember] //public Dictionary
ActionParameters1 //{ // get // { // if (actionParameters1 == null) // { // actionParameters1 = new Dictionary
(); // } // return actionParameters1; // } // set // { // actionParameters1 = value; // } //} //static Type[] GetKnowTypes() //{ // return new Type[] { typeof(Dictionary
) }; //} [DataMember] public Dictionary
Quote = new Dictionary
(); //static Type[] GetKnowTypesQuote() //{ // return new Type[] { typeof(Dictionary
) }; //} }
      填充实体的代码如下:

ActionInfo ac = new ActionInfo();            ac.ActionId = "ActionId" + Guid.NewGuid().ToString();            ac.ActionName = "ActionName" + Guid.NewGuid().ToString();            ac.ActionParameters = new Dictionary
(); for (int i = 0; i < 2; i++) { ActionParameterInfo ap = new ActionParameterInfo(); ap.ParameterAllowDBNull = false; ap.ParameterCategory = ActionInfoParameterCategoryEnum.CommonType; ap.ParameterValue = Guid.NewGuid(); ac.ActionParameters.Add(ap.ParameterValue.ToString(), ap); } //ac.ActionParameters1 = new Dictionary
(); //for (int i = 0; i < 2; i++) //{ // ActionParameterInfo ap = new ActionParameterInfo(); // ap.ParameterAllowDBNull = false; // ap.ParameterCategory = ActionInfoParameterCategoryEnum.CommonType; // ap.ParameterValue = Guid.NewGuid(); // ac.ActionParameters1.Add(ap.ParameterValue.ToString(), ap); //} ac.Quote = new Dictionary
(); for (int i = 0; i < 2; i++) { string str = Guid.NewGuid().ToString(); int f = i; ((Dictionary
)ac.Quote).Add(str, f); }
       序列化之后的结果为:

ActionIdb4df91db-7ba1-480f-bc6f-2dfa3eb27ae7
ActionName30be2364-53d6-429d-8ca4-61dd8928b779
240b226f-8aa0-48e7-a24f-0640e2da7172
<参数值 i:type="b:guid" xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/">
240b226f-8aa0-48e7-a24f-0640e2da7172
<参数是否允许为空>
false
<参数分类>
CommonType
73085803-8d35-4b52-a550-c354f0dae7a0
<参数值 i:type="b:guid" xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/">
73085803-8d35-4b52-a550-c354f0dae7a0
<参数是否允许为空>
false
<参数分类>
CommonType
d9298e16-e41f-4508-a7dc-6a26e137ec2d
0
1a53f746-60fe-4b0b-8c48-479693ad46fa
1
           
可见,可以通过多个KnownType标识来实现同一个数据契约中多个复杂类型的序列化与反序列化。

此处是以两个字典类型来验证的,其实不管是list还是字典都可以通过:多个KnownType标识来实现同一个数据契约中多个复杂类型的序列化与反序列化。 

KnownType标识类简洁处理方式(这种方式不需要在类上标识KnownType):

List
knownTypes = new List
();knownTypes.Add(typeof(Dictionary
));knownTypes.Add(typeof(List
));
   
   然后调用:

DataContractSerializer
.ToXMLString(ac, knownTypes);
         
即可

  序列化反序列化代码:

       Demo代码下载:

  DataContractJsonSerializer的用法与DataContractSerializer类似,只是两者对于实体类序列化后的存储形式不一样,仅此而已。

小注:

如果序列化list<baseclass>即需要序列化一个由baseclass类及其子类组成的集合的时候,需要在子类基类上加上标识

[KnownType(typeof(EndEvent))]public class EndEvent:BpmnEvent
在反序列化的时候,调用:

public static T ReadFromXML(string xmlStr, IEnumerable
knownTypes = null)
 传入子类基类的的Type

参考文章:

你可能感兴趣的文章
nginx rewrite
查看>>
前端安全系列(一):如何防止XSS攻击?
查看>>
用Mysql5.6出现时间问题Incorrect datetime value: '' for column 'createtime'
查看>>
我的友情链接
查看>>
Pureftpd的权限控制
查看>>
RHEL6 64位ASM方式安装oracle 11gR2(二)
查看>>
微信授权登录
查看>>
IK分词器安装
查看>>
查看Linux并发连接数
查看>>
带有加解密通信的应用安装为windows服务时,需要注意使用的账户
查看>>
发送手机验证码需要注意点问题
查看>>
Android Studio提示"licences have not been accepted"
查看>>
你是谁不重要,关键是你跟谁!
查看>>
CSS中规则@media的用法
查看>>
pychecker:分析你的python代码
查看>>
关于linux上安装网络打印机
查看>>
css 默认不显示 之后显示
查看>>
Django博客项目之登录和注册系统
查看>>
QPS从0到4000请求每秒,谈达达后台架构演化之路
查看>>
我的友情链接
查看>>