最近因为项目需求,须要读写操做XML文件,而且存储的XML文件格式会随着导入的数据不一样而随时改变(固然导入的数据仍是有必定约束的),这样咱们要预先定义好XML文件的格式就不太现实了,如何实现无论导入的数据如何变化,我都能正确的把数据解析出来,这就是要实现的动态的XML文件读写操做!若是你们有更好的方式欢迎交流!html
本文所实现的读写XML文件是使用序列话的方式,具体博文请参考:http://www.cnblogs.com/fish-li/archive/2013/05/05/3061816.html,固然若是只是序列化操做XML文件的话,只须要看这篇博文,也就不须要这篇文章了!web
了解了如何序列化操做XML文件了话,你就会知道,要想写入和读取XML文件,咱们就须要定义好一个数据类型(这一点很重要),可是问题就出现了,若是咱们在编程时就定义好一个数据类型了,当导入的数据改变了,这个数据类型就不适合了,接下来就来解决这个问题编程
废话先很少说,先上代码,代码说话,每每比语言来的直接也更容易懂性能
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Reflection; using System.IO; using System.Diagnostics; namespace XMLDemo { public class ReflectClass { //保存动态生成并编译的类的type对象 Type theType = null; //保存动态生成类的实例 object theClass = null; public Assembly DoOperation() { //未初始化 if (theType == null) { //初始化 return GenerateCode(); } return null; } private Assembly GenerateCode() { //文件名 string fileName = "XmlClass"; ////从编译好的dll文件load一个Assembly //Assembly a = Assembly.LoadFrom(fileName + ".dll"); //从编译好的dll文件load一个Assembly byte[] filedata = File.ReadAllBytes(fileName + ".dll"); Assembly a = Assembly.Load(filedata); //此方法在使用完dll文件后会自动释放资源 return a; } public Assembly DoOperation(List<ExcelData> list) { //未初始化 if (theType == null) { //初始化 return GenerateCode(list); } return null; } private Assembly GenerateCode(List<ExcelData> list) { //文件名 string fileName = "XmlClass"; //打开文件,若是不存在,则建立 Stream s = File.Open(fileName + ".cs", FileMode.Create); //建立一个StreamWriter来写入数据 StreamWriter wrtr = new StreamWriter(s); #region 写入动态建立类的源代码 wrtr.WriteLine("// 动态建立的XmlClass类文件"); //类名,此类是为序列化读取XML文件建立的类文件 string className = "XmlClass"; wrtr.WriteLine("using System;"); wrtr.WriteLine("using System.Xml.Serialization;"); wrtr.WriteLine("public class {0}", className); wrtr.WriteLine("{"); var ch = (from num in list select num.Mark).Distinct().ToList(); foreach (var n in ch) { wrtr.WriteLine("\tpublic " + n + " " + n); wrtr.WriteLine("\t{"); wrtr.WriteLine("\t\tget;set;"); wrtr.WriteLine("\t}"); } wrtr.WriteLine("}"); foreach (var n in ch) { wrtr.WriteLine("public class {0}", n); wrtr.WriteLine("{"); var nlist = from num in list where (num.Mark == n) select num; foreach (var m in nlist) { wrtr.WriteLine("\tpublic string " + m.Name); wrtr.WriteLine("\t{"); wrtr.WriteLine("\t\tget;set;"); wrtr.WriteLine("\t}"); } wrtr.WriteLine("}"); } //关闭StreamWriter和文件 wrtr.Close(); s.Close(); #endregion //启动进程编译源文件 //指定参数 ProcessStartInfo psi = new ProcessStartInfo(); File.Delete(fileName + ".dll"); //从新导入数据时删除旧的DLL文件 //启动cmd.exe psi.FileName = "cmd.exe"; //cmd.exe的参数,/c-close,完成后关闭;后为参数,指定cmd.exe使用csc来编译刚才生成的源文件 string compileString = "/c C:\\WINDOWS\\Microsoft.NET\\Framework\\v4.0.30319\\csc.exe /optimize+ /target:library {0}.cs"; psi.Arguments = String.Format(compileString, fileName); //运行时的风格-最小化 psi.WindowStyle = ProcessWindowStyle.Minimized; //启动进程 Process proc = Process.Start(psi); //指定当前在此进程退出前等待 proc.WaitForExit(); //从编译好的dll文件load一个Assembly byte[] filedata = File.ReadAllBytes(fileName + ".dll"); Assembly a = Assembly.Load(filedata); //此方法在使用完dll文件后会自动释放资源 //Assembly a = Assembly.LoadFrom(fileName + ".dll"); //这样使用dll文件会一直被占用,不会释放,形成从新生成前没法自动删除 //删除源文件 //File.Delete(fileName + ".cs"); return a; } } }
以上 ReflectClass类帮助咱们实现了经过导入的数据来建立一个供咱们使用的编译好的类文件,下面主要讲若是经过这个使用这个编译好的dll文件来写入咱们的XML文件spa
写入动态建立类的源代码那一块真是费了我很多脑细胞,感受跟写模板相似的!(你们若是根据本身需求改写的话,应该就会体会到)调试
仍是先上代码,XMLHelper帮助类能够去上面的链接去下载,也能够在我提供的Demo里下载,我只根据个人须要添加了一个方法,下面个人一个普通帮助类CommonHelpercode
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; namespace XMLDemo { public class CommonHelper { /// <summary> /// 写文件 /// </summary> /// <param name="filePath">文件名</param> /// <param name="Context">写的内容</param> /// <returns>是否成功</returns> public static bool FileWriter(string filePath, string Context) { try { //打开文件,若是不存在,则建立 Stream s = File.Open(filePath, FileMode.Create); //建立一个StreamWriter来写入数据 StreamWriter wrtr = new StreamWriter(s); //写入动态建立类的源代码 wrtr.WriteLine(Context); wrtr.Close(); s.Close(); return true; } catch (Exception) { return false; } } /// <summary> /// 读文件 /// </summary> /// <param name="filePath">文件名</param> /// <returns>返回读的内容</returns> public static string FileRead(string filePath) { try { //打开文件,若是不存在,则建立 Stream s1 = File.Open(filePath, FileMode.Open); //建立一个StreamWriter来写入数据 StreamReader reader = new StreamReader(s1); //写入动态建立类的源代码 string xml = reader.ReadToEnd(); reader.Close(); s1.Close(); return xml; } catch (Exception) { return null; } } /// <summary> /// 根据数据建立类,并获得动态建立类的集合 /// </summary> /// <param name="list">导入的数据集合</param> /// <returns>返回类的集合</returns> public static List<object> GetXmlClassInstances(List<ExcelData> list) { List<object> o = new List<object>(); ReflectClass t = new ReflectClass(); var assemblys = t.DoOperation(list); Type[] types = assemblys.GetExportedTypes(); foreach (Type type in types) { o.Add(assemblys.CreateInstance(type.Name)); } return o; } /// <summary> /// 获得动态建立类的集合 /// </summary> /// <returns></returns> public static List<object> GetXmlClassInstances() { List<object> o = new List<object>(); ReflectClass t = new ReflectClass(); var assemblys = t.DoOperation(); Type[] types = assemblys.GetExportedTypes(); foreach (Type type in types) { o.Add(assemblys.CreateInstance(type.Name)); } return o; } } }
主要是一个GetXmlClassInstances(List<ExcelData> list):根据数据建立类,并获得动态建立类的集合orm
一个是GetXmlClassInstances():获得已经建立好的动态类的集合xml
下面是一个数据实体类,也就是咱们所规范的数据格式htm
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace XMLDemo { /// <summary> /// 数据实体类 /// </summary> public class ExcelData { /// <summary> /// 根节点 /// </summary> public string Mark { get; set; } /// <summary> /// 子节点 /// </summary> public string Name { get; set; } /// <summary> /// 子节点对应值 /// </summary> public string Value { get; set; } } }
准备工做就绪后就能够进行XML文件的写入了,依然是废话很少说,上代码:
private void btnWrite_Click(object sender, EventArgs e) { List<object> XmlClassInstances = CommonHelper.GetXmlClassInstances(listData); if (InsertXmlClass(XmlClassInstances, listData) == false) { MessageBox.Show("数据导入失败"); return; } } private bool InsertXmlClass(List<object> o, List<ExcelData> list) { for (int i = 1; i < o.Count; i++) { //给类的属性赋值 var nlist = from num in list where (num.Mark == o[i].GetType().Name) select num; foreach (var model in nlist) { PropertyInfo propinfo = o[i].GetType().GetProperty(model.Name); propinfo.SetValue(o[i], model.Value, null); } } var XmlClass = o[0]; //默认第一个类为主体类,主体类的属性为其余类 var tt = XmlClass.GetType(); for (int i = 1; i < o.Count; i++) { //给主体类的属性赋值 PropertyInfo propinfo = tt.GetProperty(o[i].GetType().Name); propinfo.SetValue(XmlClass, o[i], null); } //序列化主体类,获得序列化字符串 string xml = XmlHelper.XmlSerialize(XmlClass, Encoding.UTF8); string FilePath = "QMSPlan.XML"; if (CommonHelper.FileWriter(FilePath, xml) == false) { return false; } return true; }
上面的代码可能有点绕,你们能够单步调试去理解,当时写的时候崩溃了几回,最后才调试好,不知道有什么更好的写法!
感受用语言都表达不出来,仍是直接上代码吧
private void btnRead_Click(object sender, EventArgs e) { List<ExcelData> listRead = new List<ExcelData>(); var XmlClass = GetXmlClass(); if (XmlClass == null) { MessageBox.Show("数据读取失败"); return; } //获取全部主体类的属性(即全部的子类集合) PropertyInfo[] ps = XmlClass.GetType().GetProperties(); foreach (var n in ps) { //获得子类 var m = n.GetValue(XmlClass, null); //获取子类的全部属性 PropertyInfo[] x = m.GetType().GetProperties(); foreach (var y in x) { ExcelData model = new ExcelData(); model.Mark = m.GetType().Name; model.Name = y.Name; model.Value = y.GetValue(m, null).ToString(); listRead.Add(model); } } } private object GetXmlClass() { List<object> o = CommonHelper.GetXmlClassInstances(); var XmlClass = o[0]; //默认第一个类为主体类 string FilePath = "QMSPlan.XML"; string xml = CommonHelper.FileRead(FilePath); if (xml == null) { return null; } XmlClass = XmlHelper.XmlDeserialize(XmlClass.GetType(), xml, Encoding.UTF8); return XmlClass; }
上面大量运用了反射的特性,就是由于实现动态读取数据类型而形成的!
可能你们看完以为彻底不必这样作,而且大量这样的操做会影响性能,不过这样作实实在在解决了我项目中的难点,实现了根据导入数据的不一样,能够实现正确的XML的读写操做,
总结一下:以上总共有三个知识点的应用
1.动态建立数据类型并在程序中使用
2.序列话读写XML文件
3.利用反射特性实现
最后附上demo下载地址:http://files.cnblogs.com/beimeng/XML.demo.rar