最近因爲項目需求,須要讀寫操做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