有些时候,需要将一个类保存到文件用,C# 支持三种序列化:BinaryFormatter、SoapFormatter、XmlSerializer。
BinaryFormatter,不能手动修改数据,适合于 .NET 程序间交换数据。
SoapFormatter,可以手动修改数据,适合于 .NET 程序间交换数据。
XmlSerializer,这个可读性很强,适合于跨平台、跨语种交换数据,还可以用来保持配置文件。

XmlSerializer 的例子很多,可以参看MSDN上面的,这里就不再复制粘贴了。

由于某些项目的需要频繁使用XML交换数据,且类类型多样,如果每个都手动写代码转换,是很浪费时间的(虽然看上去代码很多,老板会认为有很多工作量。),所以写了一个模板类,以及 .NET 3.0 以后支持的扩展方法。

using System;
using System.Text;
using System.IO;
using System.Xml.Serialization;
using System.Diagnostics;
using System.Collections;
using System.Runtime.CompilerServices;

namespace com.hetaoos.Common.Utils
{
    [Serializable]
    public abstract class XmlSerializerBase<TClass>
        where TClass : class
    {

        /// <summary>
        /// 将该类转换为 XML 文本
        /// </summary>
        /// <returns></returns>
        public string ToXmlString()
        {
            using (MemoryStream ms = new MemoryStream())
            {
                XmlSerializer serializer = GetXmlSerializer();
                try
                {
                    serializer.Serialize(ms, this);
                    ms.Close();
                    return Encoding.UTF8.GetString(ms.ToArray());
                }
                catch (Exception ex)
                {
                    Debug.Print(ex.ToString());
                    return string.Empty;
                }
            }
        }

        /// <summary>
        /// 保存当前对象到文件
        /// </summary>
        /// <param name="filePath"></param>
        /// <returns></returns>
        public bool WriteObjectToFile(string filePath)
        {
            try
            {
                File.WriteAllText(filePath, ToXmlString());
                return true;
            }
            catch (Exception exc)
            {
                Debug.Print(exc.ToString());
                return false;
            }
        }

        /// <summary>
        /// 从 XML 文件中还原对象
        /// </summary>
        /// <param name="filePath"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        public static object ReadObjectFromFile(string filePath, Type type)
        {
            try
            {
                return FromXmlString(File.ReadAllText(filePath), type);
            }
            catch (Exception exc)
            {
                Debug.Print(exc.ToString());
                return null;
            }
        }

        /// <summary>
        /// 从 XML 字符串中还原对象
        /// </summary>
        /// <param name="xmlString"></param>
        /// <returns></returns>
        public static TClass FromXmlString(string xmlString)
        {
            return FromXmlString(xmlString, typeof(TClass)) as TClass;
        }

        /// <summary>
        /// 将对象转换为 XML 字符串
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static string ToXmlString(TClass obj)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                XmlSerializer serializer = GetXmlSerializer();
                try
                {
                    serializer.Serialize(ms, obj);
                    ms.Close();
                    return Encoding.UTF8.GetString(ms.ToArray());
                }
                catch (Exception ex)
                {
                    Debug.Print(ex.ToString());
                    return string.Empty;
                }
            }
        }

        /// <summary>
        /// 保存对象到文件
        /// </summary>
        /// <param name="filePath"></param>
        /// <returns></returns>
        public static bool WriteObjectToFile(TClass obj, string filePath)
        {
            try
            {
                File.WriteAllText(filePath, ToXmlString(obj));
                return true;
            }
            catch (Exception exc)
            {
                Debug.Print(exc.ToString());
                return false;
            }
        }
        /// <summary>
        /// 从 XML 文件中还原对象
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public static TClass ReadObjectFromFile(string fileName)
        {
            return ReadObjectFromFile(fileName, typeof(TClass)) as TClass;
        }

        /// <summary>
        /// 从 XML 字符串中还原对象
        /// </summary>
        /// <param name="xmlString"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        public static object FromXmlString(string xmlString, Type type)
        {
            if (string.IsNullOrEmpty(xmlString))
            {
                return null;
            }
            using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(xmlString)))
            {
                XmlSerializer serializer = GetXmlSerializer(type);
                try
                {
                    return serializer.Deserialize(stream);
                }
                catch
                {
                    return null;
                }
            }

        }

        /// <summary>
        /// 设置默认值
        /// </summary>
        public virtual void SetDefault()
        {
        }

        private static Hashtable XmlSerializerCache = new Hashtable();
        /// <summary>
        /// 序列化转换器缓存,避免重复创建
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        [MethodImpl(MethodImplOptions.Synchronized)]
        public static XmlSerializer GetXmlSerializer(Type type)
        {
            lock (XmlSerializerCache)
            {
                XmlSerializer serializer = XmlSerializerCache[type] as XmlSerializer;
                if (serializer == null)
                {
                    serializer = new XmlSerializer(type);
                    XmlSerializerCache[type] = serializer;
                }
                return serializer;
            }
        }

        /// <summary>
        /// 获取当前类的序列话器
        /// </summary>
        /// <returns></returns>
        public static XmlSerializer GetXmlSerializer()
        {
            return GetXmlSerializer(typeof(TClass));
        }
    }

    #region 扩展类
    public static class XmlSerializerEx
    {
        /// <summary>
        /// 将对象转换为 XML 字符串
        /// </summary>
        /// <typeparam name="TClass"></typeparam>
        /// <param name="t"></param>
        /// <returns></returns>
        public static string ToXmlString<TClass>(this TClass t)
            where TClass : class
        {
            using (MemoryStream ms = new MemoryStream())
            {
                XmlSerializer serializer = XmlSerializerBase<TClass>.GetXmlSerializer();
                try
                {
                    serializer.Serialize(ms, t);
                    ms.Close();
                    return Encoding.UTF8.GetString(ms.ToArray());
                }
                catch (Exception ex)
                {
                    Debug.Print(ex.ToString());
                    return string.Empty;
                }
            }
        }

        /// <summary>
        /// 将 XML 字符串转换为对象
        /// </summary>
        /// <typeparam name="TClass"></typeparam>
        /// <param name="t"></param>
        /// <param name="xmlString"></param>
        /// <returns></returns>
        public static TClass FromXmlString<TClass>(this TClass t, string xmlString)
            where TClass : class
        {
            if (string.IsNullOrEmpty(xmlString))
            {
                return null;
            }
            using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(xmlString)))
            {
                XmlSerializer serializer = XmlSerializerBase<TClass>.GetXmlSerializer();
                try
                {
                    return serializer.Deserialize(stream) as TClass;
                }
                catch
                {
                    return null;
                }
            }
        }
    }
    #endregion
}

在需要XML序列化的类中,继承基类 XmlSerializerBase,即可。如

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
using System.IO;

namespace com.hetaoos.Configurations
{
    /// <summary>
    /// 本地配置文件
    /// </summary>
    [DisplayName("本地配置文件")]
    public class AppConfig : XmlSerializerBase<AppConfig>
    {
        /// <summary>
        /// 遇到错误自动重启
        /// </summary>
        [XmlElement(ElementName = "OnErrorAutoReset")]
        public bool OnErrorAutoReset { get; set; }

        public AppConfig()
        {

        }

        public override void SetDefault()
        {
            OnErrorAutoReset = true;
        }

    }
}

当然,如果目标类已经集成有一个基类了,可以使用静态方法,如:

//快速用法
AppConfig oldConfig = new AppConfig() { OnErrorAutoReset = true };
string xmlString = XmlSerializerBase<AppConfig>.ToXmlString(oldConfig);
AppConfig newConfig = XmlSerializerBase<AppConfig>.FromXmlString(xmlString);

//.NET 3.0 以后的扩展方法
xmlString = oldConfig.ToXmlString<AppConfig>();
//注意,这个返回的是一个新的对象,和 oldConfig 无关
newConfig = oldConfig.FromXmlString<AppConfig>(xmlString);

值得注意的是,在.NET 中创建的 序列化器 XmlSerializer 在使用结束后并其内部有个对象不会被销毁。也不清楚它什么时候会被销毁。目前观测到的结果是,显示调用垃圾回收方法GC.Collect(); 也不能将其销毁。
由于每种类型对应一个 XmlSerializer,但相同类型的对象来时,只要调用缓存中的序列化器,没有的时候才创建新的。于是,代码中加入了 序列化器缓存 XmlSerializerCache。

Did you like this? Share it:

关联文章:

  1. C# 中利用反射机制拷贝类的字段和属性
  2. 动态填充/修改类属性的 DisplayNameAttribute 属性
  3. Linux 下 C++ 的多线程基类 – Thread