使用XmlSerializer类将对象序列化为Xml格式保存_支持泛型的Dictionary

一个特殊应用中,我们需要将内存中的一个对象持久化,而这个对象是来自一个模板类实例化出来的,不能保存到数据库中,数据库中只存有此对象的模板.

由于使用到泛型的Dictionary,而XmlSerializer却不支持认的泛型的Dictionary,为此我找了些资料,并在此文中以三种不同的方式实现. 本文中约定:

方案1: 不序列化泛型的Dictionary

方案2: 定义支持泛型的Dictionary

方案3: 让每个类实现IXmlSerializable接口

本文内容:
1. 类图及类之间的关联
2. 方案1: 不序列化泛型的Dictionary
3. 方案2: 定义支持泛型的Dictionary
4. 方案3: 让每个类实现IXmlSerializable接口
5. 总结


1. 类图及类之间的关联

薪酬模板PayTemplate,它通过Dictionary<int,PayItemTemplate>维持着多个薪酬栏目模板PayItemTemplate.

一个PayItemTemplate通过Dictionary<int,PayItemTemplate>记录着那些使用过它的PayItemTemplate.

所以,PayTemplate与PayItemTemplate是一对多关联; PayItemTemplate是一对多的自关联.

方案1,2,3中的类关联都相似,这里就不重复了. 详细差别可在本文末尾下载源代码查看.



2. 方案1: 不序列化泛型的Dictionary
既然知道了泛型的Dictionary不被XmlSerializer支持,我们就避免泛型的Dictionary被序列化,只需要在字段上加上XmlIgnore属性即可.代码如下:

46 [XmlIgnore]//带有XmlIgnore,表示序列化时不序列化此属性

47 publicDictionary<int,PayItemTemplate> PayItemTemplateDic

48 {

49 get{returnpayItemTemplates; }

50 set{ payItemTemplates =value; }

51 }

好了,既然让泛型的Dictionary不被序列化了,而我们的需求中又需要将泛型的Dictionary中的对象序列化到Xml中保存,那怎么办呢?这里的办法就是加多一个额外的PayItemTemplate[]数组字段,代码实现如下:

53 ///<summary>

54 ///用于序列化PayItemTemplate集合

55 ///</summary>

56 publicPayItemTemplate[] PayItemTemplates

57 {

58 get

59 {

60 List<PayItemTemplate> list =newList<PayItemTemplate>(payItemTemplates.Count);

61 foreach(keyvaluePair<int,PayItemTemplate> pitinpayItemTemplates)

62 {

63 list.Add(pit.Value);

64 }

65

66 returnlist.ToArray();

67 }

68 set

69 {

70 payItemTemplates =newDictionary<int,PayItemTemplate>();

71 foreach(PayItemTemplatepitinvalue)

72 {

73 payItemTemplates.Add(pit.Id,pit);

74 }

75 }

76 }

这就是我们需要做的,下面进行测试,具体的单元测试,可在下载源代码中查看.
测试结果:
正向序列化:

<?xmlversion="1.0"encoding="utf-8"?>

<PayTemplatexmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<Id>100</Id>

<Name>薪酬模版Test</Name>

<StartDate>2007-08-16T19:10:43.640625+08:00</StartDate>

<PayItemTemplates>

<PayItemTemplate>

<numbericalCategory>Calculational</numbericalCategory>

<NumbericalCategory>Calculational</NumbericalCategory>

<Id>10000</Id>

<Name>薪酬模板栏目1</Name>

<PayItemTemplateRelieds>

<PayItemTemplate>

<numbericalCategory>Calculational</numbericalCategory>

<NumbericalCategory>Calculational</NumbericalCategory>

<Id>20000</Id>

<Name>薪酬模板栏目2</Name>

<PayItemTemplateRelieds/>

<Enabled>true</Enabled>

<Expression>Expression2</Expression>

</PayItemTemplate>

</PayItemTemplateRelieds>

<Enabled>true</Enabled>

<Expression>Expression</Expression>

</PayItemTemplate>

<PayItemTemplate>

<numbericalCategory>Calculational</numbericalCategory>

<NumbericalCategory>Calculational</NumbericalCategory>

<Id>20000</Id>

<Name>薪酬模板栏目2</Name>

<PayItemTemplateRelieds/>

<Enabled>true</Enabled>

<Expression>Expression2</Expression>

</PayItemTemplate>

</PayItemTemplates>

<WorkFlowCategory>Simple</WorkFlowCategory>

</PayTemplate>

反向序列化:

方案1优缺点: 不用定义额外的对象,但出现多余的字段(多余是因为此字段只是为了序列化而出现的).


3. 方案2: 定义支持泛型的Dictionary

是否能让泛型的Dictionary能够被正常序列化呢? 是,重新定义一个泛型的SerializableDictionary,让它继承.Net 中的泛型Dictionary并实现IXmlSerializable接口,本文中的实现参考自XML Serializable Generic Dictionary. IXmlSerializable接口中的两个关键方法:

25 ///<summary>

26 ///反序列化

27 ///</summary>

28 ///<param name="reader"></param>

29 publicvoidreadxml(XmlReaderreader)

30 {

31 XmlSerializerkeySerializer =newXmlSerializer(typeof(TKey));

32 XmlSerializervalueSerializer =newXmlSerializer(typeof(TValue));

33 if(reader.IsEmptyElement || !reader.Read())

34 {

35 return;

36 }

37

38 while(reader.NodeType !=XmlNodeType.EndElement)

39 {

40 reader.ReadStartElement("item");

41

42 reader.ReadStartElement("key");

43 TKey key = (TKey)keySerializer.Deserialize(reader);

44 reader.ReadEndElement();

45

46 reader.ReadStartElement("value");

47 TValue value = (TValue)valueSerializer.Deserialize(reader);

48 reader.ReadEndElement();

49

50 reader.ReadEndElement();

51 reader.MovetoContent();

52

53 this.Add(key,value);

54 }

55 reader.ReadEndElement();

56 }

57

58 ///<summary>

59 ///序列化

60 ///</summary>

61 ///<param name="writer"></param>

62 publicvoidWriteXml(XmlWriterwriter)

63 {

64 XmlSerializerkeySerializer =newXmlSerializer(typeof(TKey));

65 XmlSerializervalueSerializer =newXmlSerializer(typeof(TValue));

66

67 foreach(TKey keyinthis.Keys)

68 {

69 writer.WriteStartElement("item");

70

71 writer.WriteStartElement("key");

72 keySerializer.Serialize(writer,key);

73 writer.WriteEndElement();

74

75 writer.WriteStartElement("value");

76 valueSerializer.Serialize(writer,this[key]);

77 writer.WriteEndElement();

78

79 writer.WriteEndElement();

80 }

81 }



完整代码请到源文件中查看.

测试结果:
正向序列化:

<?xmlversion="1.0"encoding="utf-8"?>

<PayTemplateV2xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<Id>100</Id>

<Name>薪酬模版Test</Name>

<StartDate>2007-08-16T19:08:45.0625+08:00</StartDate>

<PayItemTemplates>

<item>

<key>

<int>10000</int>

</key>

<value>

<PayItemTemplateV2>

<numbericalCategory>Calculational</numbericalCategory>

<NumbericalCategory>Calculational</NumbericalCategory>

<Id>10000</Id>

<Name>薪酬模板栏目1</Name>

<PayItemTemplateReliedList>

<item>

<key>

<int>20000</int>

</key>

<value>

<PayItemTemplateV2>

<numbericalCategory>Calculational</numbericalCategory>

<NumbericalCategory>Calculational</NumbericalCategory>

<Id>20000</Id>

<Name>薪酬模板栏目2</Name>

<PayItemTemplateReliedList/>

<Enabled>true</Enabled>

<Expression>Expression2</Expression>

</PayItemTemplateV2>

</value>

</item>

</PayItemTemplateReliedList>

<Enabled>true</Enabled>

<Expression>Expression</Expression>

</PayItemTemplateV2>

</value>

</item>

<item>

<key>

<int>20000</int>

</key>

<value>

<PayItemTemplateV2>

<numbericalCategory>Calculational</numbericalCategory>

<NumbericalCategory>Calculational</NumbericalCategory>

<Id>20000</Id>

<Name>薪酬模板栏目2</Name>

<PayItemTemplateReliedList/>

<Enabled>true</Enabled>

<Expression>Expression2</Expression>

</PayItemTemplateV2>

</value>

</item>

</PayItemTemplates>

<WorkFlowCategory>Simple</WorkFlowCategory>

</PayTemplateV2>



反向序列化:



方案2中我们定义了SerializableDictionary并实现了IXmlSerializable接口,惟一的不足就是需要将源代码中的所有需要序列化的泛型Dictionary改为SerializableDictionary. 好在有VS2005开发工具帮忙,只需要点击查找-全部替换就行了; 并且SerializableDictionary具有可重用性.

4. 方案3: 让每个类实现IXmlSerializable接口

此方案类似于方案2,只是没有重新定义新的Dictionary,而是让每个带有泛型Dictionary且需要Xml序列化的类实现IXmlSerializable接口,自行定义Xml序列化逻辑. 实现代码与方案2中的类似. 这里就省略了,具体可下载源代码查看.

测试结果:
正向序列化:

<?xmlversion="1.0"encoding="utf-8"?>

<PayTemplateV3>

<Id>100</Id>

<Name>薪酬模版Test</Name>

<StartDate>2007年8月17日</StartDate>

<WorkFlowCategory>Simple</WorkFlowCategory>

<PayItemTemplates>

<PayItemTemplateV3>

<Id>10000</Id>

<Name>薪酬模板栏目1</Name>

<Expression>Expression</Expression>

<Enabled>True</Enabled>

<NumbericalCategory>Calculational</NumbericalCategory>

<PayItemTemplateReliedList>

<PayItemTemplateV3>

<Id>20000</Id>

<Name>薪酬模板栏目2</Name>

<Expression>Expression2</Expression>

<Enabled>True</Enabled>

<NumbericalCategory>Calculational</NumbericalCategory>

<PayItemTemplateReliedList/>

</PayItemTemplateV3>

</PayItemTemplateReliedList>

</PayItemTemplateV3>

<PayItemTemplateV3>

<Id>20000</Id>

<Name>薪酬模板栏目2</Name>

<Expression>Expression2</Expression>

<Enabled>True</Enabled>

<NumbericalCategory>Calculational</NumbericalCategory>

<PayItemTemplateReliedList/>

</PayItemTemplateV3>

</PayItemTemplates>

</PayTemplateV3>



反向序列化:



为什么我还要使用方案3呢? 因为在序列化时有可能会出现循环引用的情况,一旦出现了,我们就只能使用方案3了. 又或者在序列化时,有些类是需要进行特殊处理的,我们都可以采用此方法来实现. 方案3显然是灵活性最高的,但代码工作量较多.


5. 总结

使用.Net 2.0中的XmlSerializer类,可以方便地将对象转换成Xml格式,本文介绍简单一个序列化应用需求,而XmlSerializer还有许多更高级的功能,如串行化属性设置等.
XmlSerializer并不直接支持泛型Dictionary(Dictionary在XML序列化时遇到的问题及应对方案),所有我在方案1中,公开了一个数组属性,只是用于序列化时使用; 而方案2中,重新定义了一个支持Xml序列化的泛型Dictionary,参考自XML Serializable Generic Dictionary; 方案3是最灵活但工作最多的一种方案,为每个需要Xml序列化的类实现IXmlSerializable接口,然后自定义如何序列化,过程可参考方案2.

完.

:) 希望这对你有帮助.

相关文章

php输出xml格式字符串
J2ME Mobile 3D入门教程系列文章之一
XML轻松学习手册
XML入门的常见问题(一)
XML入门的常见问题(三)
XML轻松学习手册(2)XML概念