自定义Object与XML互换(序列化)

 

反射真是个好东西,值得去深入学习.今天利用它实现对象的序列化与反序列化.由于是基于可互换的原则,在没有找到反序列化之前,抛弃了部分通用的做法.

如果只是简单的将对象序列化成为XML,本是很方便的,但在考虑需要反序列化后,不得不考虑很多的问题.

比如,序列化值类型(Type.IsValueType为true)的类型时,基本只需要简单的ToString()就可以.但如果想返序列化的话,有些类型就得去考虑,比如,日期的字符串格式.我是通过下面的方式,在确定的区域和日期格式中实现日期与字符串之间做转换:

 

  1. static DateTimeFormatInfo dtfi = new CultureInfo("zh-CN"false).DateTimeFormat;  
  2. public override void ToXml(object val, StringBuilder builder)  
  3. {  
  4.     builder.AppendFormat("{0}", ((DateTime)val).ToString(dtfi));  
  5. }  
  6.   
  7. public override Object ToObject(Type type, XmlNode node)  
  8. {  
  9.     String val = node.InnerText;  
  10.     return DateTime.Parse(val, dtfi);  
  11. }  

 

在实现功能时,集合无疑是最麻烦的,特别有了泛型之后,规则就更复杂了.加上考虑接口类型的话,就难上加难.像在确定集合中元素的类型,也不一样.如:

数组元素类型: Type.GetElementType()

泛型的T类型:Type.GetGenericArguments()

而数组的元素是一个Type,但泛型的T类型却是Type[].特别在确定System.Collections.CollectionBase的元素类型时,没有找到可从Type中反射的方法,转换使用下面的方式确定:

 

  1. System.Collections.IEnumerator tor = ((IEnumerable)val).GetEnumerator();  
  2. if (tor.MoveNext())  
  3. {  
  4.     Type t = tor.Current.GetType();  
  5. }  

 

这个其实是确定集合元素类型的通用方法,但它在反序列化时却不能使用.因为这样做只能在集合不为空时才能使用,而反序列化时,指定的集合却有可能是空的.所以这个取出来的类型,需要将它的所属程序集与类型名称保存下来,比如在节点中以itype的属性把它保存下来.这样做又增加了程序的复杂度.

同时,在反序列化接口类型的属性时,也有可能遇到相似的问题,比如如何确定接口的实现类型?这个利用反射是做不到的.那么又只好把实现类型的相关信息在序列化时保存了,那不保存到ctype属性值中去吧.

有了一大堆并不完全兼容的转换方法后,如何将代码的复杂度尽量降低呢?

在这个序列化与反序列中,我们需要确定哪些转换方法适用于哪些类型,那么就让他主动告诉调用者吧.定义一个负责转换的基类:Switcher,它的作用是定义规则,实现最通用的做法,具体的事务由更详细的实现类来确定如何进行.下面是Switcher的全部代码:

 

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Text;  
  4. using System.Reflection;  
  5. using System.Xml;  
  6.   
  7. namespace Guaik.Serialization.Xml  
  8. {  
  9.     /// <summary>  
  10.     /// 转换器  
  11.     /// </summary>  
  12.     public class Switcher  
  13.     {  
  14.         /// <summary>  
  15.         /// 得到类型的属性列表  
  16.         /// </summary>  
  17.         public virtual PropertyInfo[] GetProperties(Type t)  
  18.         {  
  19.             return t.GetProperties();  
  20.         }  
  21.   
  22.         /// <summary>  
  23.         /// 检查是否可使用此转换器  
  24.         /// </summary>  
  25.         public virtual bool IsSwitcher(Type t)  
  26.         {  
  27.             return true;  
  28.         }  
  29.   
  30.         /// <summary>  
  31.         /// 转换为MXL  
  32.         /// </summary>  
  33.         public virtual void ToXml(Int32 level, String name, Type type, Object val, StringBuilder builder)  
  34.         {  
  35.             String sp = " ".PadLeft(level * 2);  
  36.             builder.AppendFormat("{0}<{1}>{2}</{1}>", sp, name, val).AppendLine();  
  37.         }  
  38.   
  39.         public virtual void ToXml(Object val, StringBuilder builder)  
  40.         {  
  41.             builder.AppendFormat("{0}", val);  
  42.         }  
  43.   
  44.         /// <summary>  
  45.         /// 转换为对象  
  46.         /// </summary>  
  47.         public virtual Object ToObject(Type type, XmlNode node)  
  48.         {  
  49.             return Convert.ChangeType(node.InnerText, type);  
  50.         }  
  51.     }  
  52. }  

 

 

可以看到,其实主要是实现了三个方法:

IsSwitcher(Type t)  这个用来告诉使用者,你这个转换器是否适用于Type类型?

ToXml(.....)  这个用来将对象转换为XML

ToObject(Type type, XmlNode node)  负责将node中的序列化的内容转换为type类型返回

GetProperties(Type t)  现在只是简单的调用Type类型的方法返回,而以后可以定义相关的规则,可以在序列化时实现隐藏属性,重命名等规则

在Switcher中实现的方法都是相对通用的,实现类中,只需要重写相关的方法则可.比如枚举是不可以像上面那样,使用Convert.ChangeType来转换的.因此需要定义一个用于处理枚举的Switcher,如下:

 

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Text;  
  4. using System.Reflection;  
  5. using System.Xml;  
  6.   
  7. namespace Guaik.Serialization.Xml  
  8. {  
  9.     /// <summary>  
  10.     /// 枚举  
  11.     /// </summary>  
  12.     public class EnumSwitcher : Switcher  
  13.     {  
  14.         public override bool IsSwitcher(Type t)  
  15.         {  
  16.             return t.IsEnum;  
  17.         }  
  18.   
  19.         public override Object ToObject(Type type, XmlNode node)  
  20.         {  
  21.             String val = node.InnerText;  
  22.             return Enum.Parse(type, val);  
  23.         }  
  24.     }  
  25. }  

 

 

实现的内容也非常简单.由于看到实现一个不兼容的类型还是相对方便的. 而在实现将数据序列化与反序列化中,也可以保持代码的简洁,下面是实现转换的类:

 

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Text;  
  4. using System.Reflection;  
  5. using System.Xml;  
  6.   
  7. namespace Guaik.Serialization.Xml  
  8. {  
  9.     /// <summary>  
  10.     /// XML持久化  
  11.     /// </summary>  
  12.     public class XmlSerialization  
  13.     {  
  14.         private static Switcher[] Switcher = new Switcher[] {   
  15.             new EnumSwitcher(),  
  16.             new StringSwitcher(),  
  17.             new DateTimeSwitcher(),  
  18.             new ValueTypeSwitcher(),  
  19.             //new GenericSwitcher(),  
  20.             new CollectionSwitcher()  
  21.         };  
  22.   
  23.         private static PropertyInfo FindProperty(Type type, String name)  
  24.         {  
  25.             PropertyInfo[] infos = GetProperties(type);  
  26.             foreach (PropertyInfo info in infos)  
  27.             {  
  28.                 if (info.Name == name) return info;  
  29.             }  
  30.             return null;  
  31.         }  
  32.   
  33.         private static PropertyInfo[] GetProperties(Type t)  
  34.         {  
  35.             return t.GetProperties();  
  36.         }  
  37.   
  38.         private static Switcher GetSwitcher(Type t)  
  39.         {  
  40.             foreach (Switcher sw in Switcher)  
  41.             {  
  42.                 if (sw.IsSwitcher(t))  
  43.                 {  
  44.                     return sw;  
  45.                 }  
  46.             }  
  47.             return null;  
  48.         }  
  49.  
  50.         #region Xml2Object  
  51.         public static Object Xml2Object(Type type, String path)  
  52.         {  
  53.             XmlDocument doc = new XmlDocument();  
  54.             doc.Load(path);  
  55.             return Xml2Object(type, doc);  
  56.         }  
  57.   
  58.         public static Object Xml2Object(Type type, XmlDocument doc)  
  59.         {  
  60.             return Xml2Object(type, doc.SelectSingleNode(type.Name));  
  61.         }  
  62.   
  63.         public static Object Xml2Object(Type type, XmlNode root)  
  64.         {  
  65.             Switcher swr = null;  
  66.   
  67.             swr = GetSwitcher(type);  
  68.             if (swr != null)  
  69.             {  
  70.                 return swr.ToObject(type, root);  
  71.             }  
  72.             else  
  73.             {  
  74.                 Object val = null;  
  75.                 Switcher switcher = null;  
  76.                 PropertyInfo info = null;  
  77.                 Object obj = Activator.CreateInstance(type, true);  
  78.                 foreach (XmlNode node in root.ChildNodes)  
  79.                 {  
  80.                     info = FindProperty(type, node.Name);  
  81.                     if (info != null)  
  82.                     {  
  83.                         switcher = GetSwitcher(info.PropertyType);  
  84.                         val = switcher.ToObject(info.PropertyType, node);  
  85.                         if (val != null)  
  86.                         {  
  87.                             info.GetSetMethod().Invoke(obj, new object[] { val });  
  88.                         }  
  89.                     }  
  90.                 }  
  91.                 return obj;  
  92.             }  
  93.         }  
  94.         #endregion  
  95.  
  96.         #region Object2Xml  
  97.         public static string Object2Xml(Object obj)  
  98.         {  
  99.             Type t = obj.GetType();  
  100.             StringBuilder builder = new StringBuilder();  
  101.             builder.AppendLine("<?xml version=/"1.0/" encoding=/"utf-8/" ?> ");  
  102.   
  103.             builder.AppendFormat("<{0}>", t.Name).AppendLine();  
  104.   
  105.             Object2Xml(1, obj, builder);  
  106.   
  107.             builder.AppendFormat("</{0}>", t.Name).AppendLine();  
  108.   
  109.             return builder.ToString();  
  110.         }  
  111.   
  112.         public static bool Object2Xml(Int32 level, Object obj, StringBuilder builder)  
  113.         {  
  114.             Switcher swr = null;  
  115.             Type t = obj.GetType();  
  116.   
  117.             swr = GetSwitcher(t);  
  118.             if (swr != null)  
  119.             {  
  120.                 swr.ToXml(obj, builder);  
  121.                 return true;  
  122.             }  
  123.             else  
  124.             {  
  125.                 object val = null;  
  126.                 MethodInfo mi = null;  
  127.                 PropertyInfo[] infos = GetProperties(t);  
  128.                 foreach (PropertyInfo info in infos)  
  129.                 {  
  130.                     mi = info.GetGetMethod();  
  131.                     if (mi != null)  
  132.                     {  
  133.                         val = mi.Invoke(obj, null);  
  134.                         if (val != null)  
  135.                         {  
  136.                             swr = GetSwitcher(val.GetType());  
  137.                             if (swr != null)  
  138.                             {  
  139.                                 swr.ToXml(level + 1, info.Name, info.PropertyType, val, builder);  
  140.                             }  
  141.                         }  
  142.                     }  
  143.                 }  
  144.                 return false;  
  145.             }  
  146.         }  
  147.         #endregion  
  148.     }  
  149. }  

 


在这个类中,有三个重要的方法:

Object2Xml(...) 将对象转换为XML

Xml2Object(...)  将XML转换为对象,提供了方法重载,方便使用者

GetSwitcher(..)  确定哪个转换器可用于当前的转换

GetSwitcher(..)方法中,使用了一个配置表Switcher ,在这里存储可用于转换的转换器集合.这个集合的次序确定了转换器的调用.

在调用方面,也非常简单,下面是测试代码:

 

  1. //类型定义  
  2.     public class User  
  3.     {  
  4.         public string Name { getset; }  
  5.         public bool Sex { getset; }  
  6.         public int Age { getset; }  
  7.         public UserType Type { getset; }  
  8.         public DateTime CreateAt { getset; }  
  9.         public String[] Logs{get;set;}  
  10.         public IList<String> List { getset; }  
  11.         public UserCollection Users { getset; }  
  12.     }  
  13.   
  14.     public enum UserType  
  15.     {  
  16.         Login,  
  17.         Logout  
  18.     }  
  19.   
  20.     /// <summary>  
  21.     /// 数据库集合  
  22.     /// </summary>  
  23.     public class UserCollection : CollectionBase  
  24.     {  
  25.         public User this[int index]  
  26.         {  
  27.             get  
  28.             {  
  29.                 return ((User)List[index]);  
  30.             }  
  31.             set  
  32.             {  
  33.                 List[index] = value;  
  34.             }  
  35.         }  
  36.   
  37.         public User this[string name]  
  38.         {  
  39.             get  
  40.             {  
  41.                 foreach (User d in this)  
  42.                 {  
  43.                     if (d.Name.ToLower() == name.ToLower())  
  44.                     {  
  45.                         return (User)d;  
  46.                     }  
  47.                 }  
  48.                 return null;  
  49.             }  
  50.         }  
  51.   
  52.         public int Add(User value)  
  53.         {  
  54.             return (List.Add(value));  
  55.         }  
  56.   
  57.         public int IndexOf(User value)  
  58.         {  
  59.             return (List.IndexOf(value));  
  60.         }  
  61.   
  62.         public void Insert(int index, User value)  
  63.         {  
  64.             List.Insert(index, value);  
  65.         }  
  66.   
  67.         public void Remove(User value)  
  68.         {  
  69.             List.Remove(value);  
  70.         }  
  71.   
  72.         public bool Contains(User value)  
  73.         {  
  74.             // If value is not of type User, this will return false.  
  75.             return (List.Contains(value));  
  76.         }  
  77.   
  78.         protected override void OnInsert(int index, Object value)  
  79.         {  
  80.             // Insert additional code to be run only when inserting values.  
  81.         }  
  82.   
  83.         protected override void OnRemove(int index, Object value)  
  84.         {  
  85.             // Insert additional code to be run only when removing values.  
  86.         }  
  87.   
  88.         protected override void OnSet(int index, Object oldValue, Object newValue)  
  89.         {  
  90.             // Insert additional code to be run only when setting values.  
  91.         }  
  92.   
  93.         protected override void OnValidate(Object value)  
  94.         {  
  95.             if (!(value is User))  
  96.                 throw new ArgumentException("value must be of type User.""value");  
  97.         }  
  98.     }  
  99.   
  100.   
  101.   
  102. //序列化  
  103.         User user = new User();  
  104.             user.Name = "邓铭武";  
  105.             user.Sex = true;  
  106.             user.Age = 20;  
  107.             user.Type = UserType.Logout;  
  108.             user.CreateAt = DateTime.Now;  
  109.             user.Logs = new string[] {  
  110.                 "登陆",  
  111.                 "注销"  
  112.             };  
  113.             user.List = new List<String>();  
  114.             user.List.Add("Name1");  
  115.             user.List.Add("Name2");  
  116.             user.List.Add("Name3");  
  117.   
  118.             user.Users = new UserCollection();  
  119.   
  120.             User su = new User();  
  121.             su.Name = "邓铭武1";  
  122.             user.Users.Add(su);  
  123.             su = new User();  
  124.             su.Name = "邓铭武2";  
  125.             user.Users.Add(su);  
  126.             su = new User();  
  127.             su.Name = "邓铭武3";  
  128.             user.Users.Add(su);  
  129.   
  130.             string xml = XmlSerialization.Object2Xml(user);  
  131.             richTextBox1.Text = xml;  
  132.   
  133.   
  134. //反序列化  
  135.         User user = new User();  
  136.             XmlDocument doc = new XmlDocument();  
  137.             doc.LoadXml(richTextBox1.Text);  
  138.   
  139.             Object obj = (User)XmlSerialization.Xml2Object(typeof(User), doc);  

 

 

从上面可以看到,序列化与反序列化调用时,只需要调用一句代码就可以实现,过程非常方便.目前它可以支持C#类型系统中的所有基本类型,IList接口,IList<T>接口和CollectionBase集合,数组等.如果想实现其它类型的转换操作,可以通过实现Switcher重新配置XmlSerialization.Switcher表就可以达到目的.

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值