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

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

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

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

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

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

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


1. 类图及类之间的关联
image

薪酬模板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         public Dictionary<int, PayItemTemplate> PayItemTemplateDic

   48         {

   49             get { return payItemTemplates; }

   50             set { payItemTemplates = value; }

   51         }

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

   53         /// <summary>

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

   55         /// </summary>

   56         public PayItemTemplate[] PayItemTemplates

   57         {

   58             get

   59             {

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

   61                 foreach (KeyValuePair<int,PayItemTemplate> pit in payItemTemplates)

   62                 {

   63                     list.Add(pit.Value);

   64                 }

   65 

   66                 return list.ToArray();

   67             }

   68             set

   69             {

   70                 payItemTemplates = new Dictionary<int, PayItemTemplate>();

   71                 foreach (PayItemTemplate pit in value)

   72                 {

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

   74                 }

   75             }

   76         }

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

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

<PayTemplate xmlns: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         public void ReadXml(XmlReader reader)

   30         {

   31             XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));

   32             XmlSerializer valueSerializer = new XmlSerializer(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         public void WriteXml(XmlWriter writer)

   63         {

   64             XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));

   65             XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));

   66 

   67             foreach (TKey key in this.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         }



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

测试结果:
正向序列化:

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

<PayTemplateV2 xmlns: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中的类似. 这里就省略了, 具体可下载源代码查看.

测试结果:
正向序列化:

<?xml version="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.

完.

 :) 希望这对你有帮助.

本文中所使用到的源文件:
Serialize.rar

posted @ 2007-08-17 01:14  MK2  阅读(6988)  评论(4编辑  收藏  举报