最近很喜欢使用XML(C#的Linq to XML)。写个类后总想把它能用XML文件保存起来,一般我都是写个ToXElement函数然后在里面……

今天写烦了,想写一个通用的工具类,以便很方便地把一个类保存化为XML。

看看写出来的结果:

  1 /************************************************************************\
  2  * 把一个对象格式化为XML(元素),即:
  3  *     1、该XML元素的名字为该对象的类型
  4  *     2、对该对象中每一个公共属性(Property)转换为字符串(String)
  5  *         保存到XML元素的属性中
  6  * 把一个XML粘贴到一个对象中,即为上一过程的逆过程。
  7  *     
  8  * 对象信息保存到XML中(然后再保存到文件)很显然将具有很高的可读性
  9 \************************************************************************/
 10 using System;
 11 using System.Collections.Generic;
 12 using System.Linq;
 13 using System.Text;
 14 using System.Xml.Linq;
 15 namespace DotNetEx.FormatAsXml
 16 {
 17     /// <summary>
 18     /// 标识能格式化为XML,可以被用在类和结构上,不可以继承,不支持多重标记
 19     /// </summary>
 20     [AttributeUsage(AttributeTargets.Class|AttributeTargets.Struct, Inherited = false
 21         AllowMultiple = false)]
 22     public sealed class CanFormatToXmlAttribute : Attribute
 23     {
 24        public CanFormatToXmlAttribute () {   }
 25     }
 26     /// <summary>
 27     /// 标识该属性不用被格式化
 28     /// </summary>
 29     [AttributeUsage(AttributeTargets.Property,Inherited=false,
 30         AllowMultiple=false)]
 31     public sealed class DonotFormatToXmlAttribute:Attribute
 32     {
 33         public DonotFormatToXmlAttribute(){}
 34     }
 35     /// <summary>
 36     /// 格式化为XML的格式器
 37     /// </summary>
 38     public static class XmlFormater
 39     {
 40         /// <summary>
 41         /// 把一个对象格式化为XML,这个对象必须有CanFormatToXml属性(Attribute)
 42         /// </summary>
 43         /// <param name="o">要格式的对象</param>
 44         /// <returns>格式化的XML元素</returns>
 45         /// <exception type="UnsurportedTypeException">输入的对象的类型必须是支持格式化为XML的类型(有CanFormatToXml属性)</exception>
 46         /// <exception type="ArgumentNullException">输入的对象不能为NULL</exception>
 47         public static XElement ToXElement(object o){
 48             if(o==null){
 49                 throw new ArgumentNullException("o");
 50             }
 51             Type t=o.GetType();
 52             var atts= t.GetCustomAttributes(typeof(CanFormatToXmlAttribute),false);
 53             
 54             if(atts.Length==0){
 55                 throw new UnsurportedTypeException(t);
 56             }
 57             XElement xml=new XElement(t.Name);
 58             var propertyInfos=t.GetProperties();
 59             foreach (var proi in propertyInfos) {
 60                 if(proi.CanRead){
 61                     if(proi.GetCustomAttributes(typeof(DonotFormatToXmlAttribute),false)
 62                         .Length==0){
 63                         var pg = proi.GetGetMethod();
 64                         object ret = pg.Invoke(o, null);
 65                         xml.Add(new XAttribute(proi.Name, Convert.ChangeType(ret, typeof(string))));
 66                     }
 67                 }
 68             }
 69             return xml;
 70         }
 71         /// <summary>
 72         /// 从一个格式化好的XML中粘贴相关格式化信息到对象
 73         /// </summary>
 74         /// <param name="xml">输入的格式化好的XML</param>
 75         /// <param name="obj">一个目标对象,其必须支持格式化为XML(有CanFormatToXml属性)</param>
 76         public static void Pase(XElement xml, object obj){
 77             if (xml == null) {
 78                 throw new ArgumentNullException("xml");
 79             }
 80             
 81             if(obj==null){
 82                 throw new ArgumentNullException("obj");
 83             }
 84 
 85             Type type = obj.GetType();
 86             if (( type.GetCustomAttributes(typeof(CanFormatToXmlAttribute), false)
 87                     .Length == 0 ) ||
 88                 xml.Name!=type.Name
 89                 ) {
 90                 throw new UnsurportedTypeException(type);
 91             }
 92             
 93             var pis = type.GetProperties();
 94             foreach (var pi in pis) {
 95                 if(pi.CanWrite){
 96                     if(pi.GetCustomAttributes(typeof(DonotFormatToXmlAttribute),false)
 97                         .Length==0){
 98                         var psm = pi.GetSetMethod(true);
 99                         psm.Invoke(obj,
100                             new object[]{ Convert.ChangeType(
101                                 xml.Attribute(pi.Name).Value,
102                                 pi.PropertyType)}
103                                 );
104                     }
105                 }
106             }
107         }
108         /// <summary>
109         /// 不是支持格式化为XML(有CanFormatToXml属性)的类型
110         /// </summary>
111         public sealed class UnsurportedTypeException:Exception
112         {
113             public UnsurportedTypeException(Type t){
114                 Type=t;
115             }
116             public Type Type{
117                 private set;
118                 get;
119             }
120             public override string  Message
121             {
122              get 
123              { 
124                return "类型\""+Type+"\"不支持格式化为XML";
125              }
126             }
127         }
128     }
129   //
130     static class Test
131     {
132         [CanFormatToXml()]
133         class Person{
134             public Person(String name ){
135                 Name = name;
136             }
137             public string Name{
138                 get;
139                 private set;
140             }
141             public int Age{
142                 get;
143                 set;
144             }
145             public DateTime BirthDay {
146                 get;
147                 set;
148             }
149             [DonotFormatToXml()]
150             public Person Test{
151                 get;
152                 set;
153             }
154         }
155         static void Main(){
156             Person p=new Person("Hack"){
157                 BirthDay=new DateTime(1990,1,1),
158                 Age=19,
159                 Test=new Person("Test"){
160                     Age=20
161                 }
162             };
163             var x=XmlFormater.ToXElement(p);
164             Console.WriteLine(x.ToString());
165             x.Attribute("Name").Value = "Changed";
166             XmlFormater.Pase(x, p);
167             Console.WriteLine(XmlFormater.ToXElement(p).ToString());
168         }
169     }
170    //*/
171 }
172 
173 

 

那个静态类Test是用于测试的,测试结果:

<Person Name="Hack" Age="19" BirthDay="1990/1/1 0:00:00" />
<Person Name="Changed" Age="19" BirthDay="1990/1/1 0:00:00" />

还不错,能把一个对象的公有属性都写到XML元素中,反过来也能粘贴回对象。

原理分析:(代码很短很清晰,这里就不多说了)

       从对象格式化到XML时,使用反射从Type得到属性信息,再得到属性的Getter,然后调用就行了;反过来也差不多。

注意那个Person.Name属性,它的Setter是私有的,不过粘贴时也能正常工作。