cnblogs Post date: 2009-12-12 23:37
最近很喜欢使用XML(C#的Linq to XML)。写个类后总想把它能用XML文件保存起来,一般我都是写个ToXElement函数然后在里面……
今天写烦了,想写一个通用的工具类,以便很方便地把一个类保存化为XML。
看看写出来的结果:
/************************************************************************\
* 把一个对象格式化为XML(元素),即:
* 1、该XML元素的名字为该对象的类型
* 2、对该对象中每一个公共属性(Property)转换为字符串(String)
* 保存到XML元素的属性中
* 把一个XML粘贴到一个对象中,即为上一过程的逆过程。
*
* 对象信息保存到XML中(然后再保存到文件)很显然将具有很高的可读性
\************************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
namespace DotNetEx.FormatAsXml
{
/// <summary>
/// 标识能格式化为XML,可以被用在类和结构上,不可以继承,不支持多重标记
/// </summary>
[AttributeUsage(AttributeTargets.Class|AttributeTargets.Struct, Inherited = false,
AllowMultiple = false)]
public sealed class CanFormatToXmlAttribute : Attribute
{
public CanFormatToXmlAttribute () { }
}
/// <summary>
/// 标识该属性不用被格式化
/// </summary>
[AttributeUsage(AttributeTargets.Property,Inherited=false,
AllowMultiple=false)]
public sealed class DonotFormatToXmlAttribute:Attribute
{
public DonotFormatToXmlAttribute(){}
}
/// <summary>
/// 格式化为XML的格式器
/// </summary>
public static class XmlFormater
{
/// <summary>
/// 把一个对象格式化为XML,这个对象必须有CanFormatToXml属性(Attribute)
/// </summary>
/// <param name="o">要格式的对象</param>
/// <returns>格式化的XML元素</returns>
/// <exception type="UnsurportedTypeException">输入的对象的类型必须是支持格式化为XML的类型(有CanFormatToXml属性)</exception>
/// <exception type="ArgumentNullException">输入的对象不能为NULL</exception>
public static XElement ToXElement(object o){
if(o==null){
throw new ArgumentNullException("o");
}
Type t=o.GetType();
var atts= t.GetCustomAttributes(typeof(CanFormatToXmlAttribute),false);
if(atts.Length==0){
throw new UnsurportedTypeException(t);
}
XElement xml=new XElement(t.Name);
var propertyInfos=t.GetProperties();
foreach (var proi in propertyInfos) {
if(proi.CanRead){
if(proi.GetCustomAttributes(typeof(DonotFormatToXmlAttribute),false)
.Length==0){
var pg = proi.GetGetMethod();
object ret = pg.Invoke(o, null);
xml.Add(new XAttribute(proi.Name, Convert.ChangeType(ret, typeof(string))));
}
}
}
return xml;
}
/// <summary>
/// 从一个格式化好的XML中粘贴相关格式化信息到对象
/// </summary>
/// <param name="xml">输入的格式化好的XML</param>
/// <param name="obj">一个目标对象,其必须支持格式化为XML(有CanFormatToXml属性)</param>
public static void Pase(XElement xml, object obj){
if (xml == null) {
throw new ArgumentNullException("xml");
}
if(obj==null){
throw new ArgumentNullException("obj");
}
Type type = obj.GetType();
if (( type.GetCustomAttributes(typeof(CanFormatToXmlAttribute), false)
.Length == 0 ) ||
xml.Name!=type.Name
) {
throw new UnsurportedTypeException(type);
}
var pis = type.GetProperties();
foreach (var pi in pis) {
if(pi.CanWrite){
if(pi.GetCustomAttributes(typeof(DonotFormatToXmlAttribute),false)
.Length==0){
var psm = pi.GetSetMethod(true);
psm.Invoke(obj,
new object[]{ Convert.ChangeType(
xml.Attribute(pi.Name).Value,
pi.PropertyType)}
);
}
}
}
}
/// <summary>
/// 不是支持格式化为XML(有CanFormatToXml属性)的类型
/// </summary>
public sealed class UnsurportedTypeException:Exception
{
public UnsurportedTypeException(Type t){
Type=t;
}
public Type Type{
private set;
get;
}
public override string Message
{
get
{
return "类型\""+Type+"\"不支持格式化为XML";
}
}
}
}
//*
static class Test
{
[CanFormatToXml()]
class Person{
public Person(String name ){
Name = name;
}
public string Name{
get;
private set;
}
public int Age{
get;
set;
}
public DateTime BirthDay {
get;
set;
}
[DonotFormatToXml()]
public Person Test{
get;
set;
}
}
static void Main(){
Person p=new Person("Hack"){
BirthDay=new DateTime(1990,1,1),
Age=19,
Test=new Person("Test"){
Age=20
}
};
var x=XmlFormater.ToXElement(p);
Console.WriteLine(x.ToString());
x.Attribute("Name").Value = "Changed";
XmlFormater.Pase(x, p);
Console.WriteLine(XmlFormater.ToXElement(p).ToString());
}
}
//*/
}
那个静态类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是私有的,不过粘贴时也能正常工作。