热门:网页模板.net视频教程JQueryMVCjsonExtJs源码示例三级联动JQuery菜单
您现在的位置:.Net中文社区>> .Net编程>>正文内容

通用XML读写和配置(一)

发布时间:2010年06月21日点击数: 佚名

在软件开发过程中,经常使用到XML文件作为配置文件,保存一些配置信息。 为了方便对程序配置文件的读写,微软特别为.Net程序提供了程序配置文件,如web.config,App.config,这些配置文件通常会自动生成在程序启动目录下,并具有特定的格式。同时,.Net Framework还提供了一组读写配置文件的接口,这些接口被包含在命名空间System.Configuration中。 但由于接口固定,系统自带的配置文件格式不容易扩展,这些配置文件通常只能用来保存一些简单的信息,比如数据库连接信息或应用程序标识。【示例代码下载】

那如何保存复杂的配置信息呢?比如数据持久化、系统界面布局,或者是程序运行过程中产生的临时数据等,这些信息通常都需要自己定义对应的类和结构,多个类之间还存在组合、连接等关系。
一般的方法是:实现一个解析类,用来存取要保存的数据。在实现读写方法时,需要逐一的解析XML文件的节点,并在解析每个节点过程中找到对应的对象,分别赋值。此时,解析类相当于一个控制器(Controller),负责与所有数据对象(类)的交互,并组织它们的存储结构。示意图如下:
 
由于解析类和数据对象类之间存在紧耦合,存在以下不足:
1.代码无法复用。如果有新的数据对象需要配置,还得重新实现一个解析类。
2.解析类的维护成本加大。每个数据对象类的变化和更改,都需要修改解析类中的相应部分,从而增加维护成本。
3.不利于数据对象扩展。如果有新的数据也需要通过配置保存,则需要修改解析类,并调整和组织所有数据对象的存储结构。
  
基于此,不断尝试将解析类和数据对象类之间解耦,以降低代码维护成本,并希望最大程度的复用代码。通过多次的项目实践,发现如下思路:
1.各个数据对象负责解析自己所对应的XML节点,实现对该节点的写入和读取(序列化和反序列化)。
2.对象只负责解析自己,如果有子对象,则要求子对象具备自我解析接口,供上级对象调用。
3.最终的与XML文件的接口由最大一级数据对象来实现,外部通过该接口来实现XML配置文件的读写。
  
下面以一个小示例来说明,这个示例中假设要使用XML文件来配置数据库信息,数据库信息共有三类数据:数据库,数据表,列,其中,每个数据库有名称和创建日期等信息,包含0或多个数据表;数据表有名表和描述信息,由1至多个列构成;列的信息由列名、数据类型、是否为空等常用属性构成。这个示例可能没有多少实际意义(除非是用于数据持久化,但通常是不用我们自己实现了:)),只用于说明当多种数据之间具有组合关系时,如何实现较灵活的XML读写功能。类图如下:
 
在示例中,首先定义一个读写XML的接口,所有需要XML文件中存取配置的数据对象必须实现此接口。该接口的定义如下:
  1. /// <summary> 
  2.     /// XML保存和设置接口 
  3.     /// </summary> 
  4.     public interface IXmlStorage 
  5.     { 
  6.          /// <summary> 
  7.          /// 根据对应的XML节点来获取各属性值 
  8.          /// </summary> 
  9.          /// <param name="factoTypeNode">读取信息的节点</param> 
  10.         void GetValueFromXml(System.Xml.XmlNode objectNode); 
  11.         /// <summary> 
  12.         /// 将对象本身格式化为XML 
  13.         /// </summary> 
  14.         /// <param name="xmldoc">需要写入的文档</param> 
  15.         /// <param name="parentEle">生成节点的父元素节点</param> 
  16.         void SetValueToXml(System.Xml.XmlDocument xmldoc, XmlElement parentEle); 
  17.     } 
其它的数据类定义如下:
  1. /// <summary> 
  2. /// 数据库对象 
  3. /// </summary> 
  4. public class DataBase : IXmlStorage 
  5.     /// <summary> 
  6.     /// 数据库名称 
  7.     /// </summary> 
  8.     public string Name = string.Empty; 
  9.     /// <summary> 
  10.     /// 描述 
  11.     /// </summary> 
  12.     public string Description = string.Empty; 
  13.     /// <summary> 
  14.     /// 创建日期 
  15.     /// </summary> 
  16.     public DateTime CreateDate = DateTime.Now; 
  17.     /// <summary> 
  18.     /// 数据表集合 
  19.     /// </summary> 
  20.     public List<Table> TableList = new List<Table>(); 
  21.  
  22.     /// <summary> 
  23.     /// 配置文件实例 
  24.     /// </summary> 
  25.     XmlDocument xmldoc = new XmlDocument(); 
  26.  
  27.     /// <summary> 
  28.     /// 默认配置文件名称 
  29.     /// </summary> 
  30.     public static readonly string DefaultConfigXml = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + Path.DirectorySeparatorChar + "DatabaseConfig.xml"
  31.  
  32.     #region IXmlStorage Members 
  33.  
  34.     public void GetValueFromXml(XmlNode objectNode) 
  35.     { 
  36.         if (objectNode == null
  37.             return
  38.         if (objectNode.Name.Equals("database", StringComparison.CurrentCultureIgnoreCase)) 
  39.         { 
  40.             XmlAttributeCollection attCol = objectNode.Attributes; 
  41.             //从节点属性中获取值 
  42.             for (int i = 0; i < attCol.Count; i++) 
  43.             { 
  44.                 XmlAttribute att = attCol[i]; 
  45.                 if (att.Name.Equals("name", StringComparison.CurrentCultureIgnoreCase)) 
  46.                     this.Name = att.Value; 
  47.                 if (att.Name.Equals("description", StringComparison.CurrentCultureIgnoreCase)) 
  48.                     this.Description = att.Value; 
  49.                 if (att.Name.Equals("date", StringComparison.CurrentCultureIgnoreCase)) 
  50.                 { 
  51.                     try 
  52.                     { 
  53.                         this.CreateDate = DateTime.Parse(att.Value); 
  54.                     } 
  55.                     catch 
  56.                     { 
  57.                         this.CreateDate = DateTime.Now; 
  58.                     } 
  59.                 } 
  60.             } 
  61.             //先清空原来的下级节点内容(因为有可能多次重复加载) 
  62.             this.TableList.Clear(); 
  63.             //获取下一级子节点的值 
  64.             XmlNodeList nodeList = objectNode.ChildNodes; 
  65.             if (nodeList != null && nodeList.Count > 0) 
  66.             { 
  67.                 for (int j = 0; j < nodeList.Count; j++) 
  68.                 { 
  69.                     XmlNode node = nodeList[j]; 
  70.                     if (node.Name.Equals("table", StringComparison.CurrentCultureIgnoreCase)) 
  71.                     { 
  72.                         Table t = new Table(); 
  73.                         t.GetValueFromXml(node); 
  74.                         this.TableList.Add(t); 
  75.                     } 
  76.                 } 
  77.             } 
  78.         } 
  79.     } 
  80.  
  81.     public void SetValueToXml(XmlDocument xmldoc, XmlElement parentEle) 
  82.     { 
  83.         XmlElement dbEle = xmldoc.CreateElement("Database"); 
  84.         dbEle.SetAttribute("name"this.Name); 
  85.         dbEle.SetAttribute("description"this.Description); 
  86.         dbEle.SetAttribute("date"this.CreateDate.ToString()); 
  87.         xmldoc.AppendChild(dbEle);           //由于此时是根节点,故parentEle为空 
  88.         //添加下一级的子节点 
  89.         for (int i = 0; i < this.TableList.Count; i++) 
  90.         { 
  91.             Table t = this.TableList[i]; 
  92.             t.SetValueToXml(xmldoc, dbEle); 
  93.         } 
  94.     } 
  95.     #endregion 
  96.  
  97. /// <summary> 
  98. /// 数据表 
  99. /// </summary> 
  100. public class Table : IXmlStorage 
  101.     /// <summary> 
  102.     /// 表名 
  103.     /// </summary> 
  104.     public string Name = string.Empty; 
  105.     /// <summary> 
  106.     /// 描述 
  107.     /// </summary> 
  108.     public string Description = string.Empty; 
  109.     /// <summary> 
  110.     /// 列集合 
  111.     /// </summary> 
  112.     public List<Column> ColumnList = new List<Column>(); 
  113.  
  114.     #region IXmlStorage Members 
  115.  
  116.     public void GetValueFromXml(System.Xml.XmlNode objectNode) 
  117.     { 
  118.         if (objectNode == null
  119.             return
  120.         if (objectNode.Name.Equals("table", StringComparison.CurrentCultureIgnoreCase)) 
  121.         { 
  122.             XmlAttributeCollection attCol = objectNode.Attributes; 
  123.             //从节点属性中获取值 
  124.             for (int i = 0; i < attCol.Count; i++) 
  125.             { 
  126.                 XmlAttribute att = attCol[i]; 
  127.                 if (att.Name.Equals("name", StringComparison.CurrentCultureIgnoreCase)) 
  128.                     this.Name = att.Value; 
  129.                 if (att.Name.Equals("description", StringComparison.CurrentCultureIgnoreCase)) 
  130.                     this.Description = att.Value; 
  131.             } 
  132.             //先清空原来的下级节点内容(因为有可能多次重复加载) 
  133.             this.ColumnList.Clear(); 
  134.             //获取下一级子节点的值 
  135.             XmlNodeList nodeList = objectNode.ChildNodes; 
  136.             if (nodeList != null && nodeList.Count > 0) 
  137.             { 
  138.                 for (int j = 0; j < nodeList.Count; j++) 
  139.                 { 
  140.                     XmlNode node = nodeList[j]; 
  141.                     if (node.Name.Equals("column", StringComparison.CurrentCultureIgnoreCase)) 
  142.                     { 
  143.                         Column c = new Column(); 
  144.                         c.GetValueFromXml(node); 
  145.                         this.ColumnList.Add(c); 
  146.                     } 
  147.                 } 
  148.             } 
  149.         } 
  150.     } 
  151.  
  152.     public void SetValueToXml(System.Xml.XmlDocument xmldoc, System.Xml.XmlElement parentEle) 
  153.     { 
  154.         XmlElement tblEle = xmldoc.CreateElement("Table"); 
  155.         tblEle.SetAttribute("name"this.Name); 
  156.         tblEle.SetAttribute("description"this.Description); 
  157.         parentEle.AppendChild(tblEle);           //由于此时是根节点,故parentEle为空 
  158.         //添加下一级的子节点 
  159.         for (int i = 0; i < this.ColumnList.Count; i++) 
  160.         { 
  161.             Column c = this.ColumnList[i]; 
  162.             c.SetValueToXml(xmldoc, tblEle); 
  163.         } 
  164.     } 
  165.  
  166.     #endregion 
其中,作为所有数据对象中的最大(级别)的对象,DataBase除了实现XML节点读写接口外,还需要提供加载和写入XML文件的方法,供外部代码调用,这也是读写XML文件的唯一入
 
  1. #region 读取和保存XML文件 
  2.       public bool LoadFromFile(string xmlPath) 
  3.         { 
  4.             try 
  5.             { 
  6.                 this.xmldoc.Load(xmlPath); 
  7.                 XmlNode rootNode = this.xmldoc.SelectSingleNode("//Database"); 
  8.                 if (rootNode != null
  9.                 { 
  10.                     this.GetValueFromXml(rootNode); 
  11.                 } 
  12.             } 
  13.             catch (Exception ex) 
  14.             { 
  15.                 string msg = "加载配置文件失败,发生异常:" + ex.Message; 
  16.                 throw new Exception(msg); 
  17.             } 
  18.             return true
  19.         } 
  20.   
  21.         public bool SaveToFile(string xmlPath) 
  22.         { 
  23.             try 
  24.             { 
  25.                 this.xmldoc = new XmlDocument();    //重新构造一个XML文档 
  26.                 XmlDeclaration xDeclare = this.xmldoc.CreateXmlDeclaration("1.0""GB2312"null); 
  27.                 this.xmldoc.AppendChild(xDeclare); 
  28.                 this.SetValueToXml(this.xmldoc, null); 
  29.                 this.xmldoc.Save(xmlPath); 
  30.                 return true
  31.             } 
  32.             catch (XmlException ex) 
  33.             { 
  34.                 throw new Exception("保存配置文件失败,发生异常:" + ex.Message); 
  35.             } 
  36.         } 
  37.   
  38.         #endregion 
完成了所有数据对象的定义,我们就可以通过XML文件来配置和存储这些数据。
示例代码如下:
 
  1. DataBase db = new DataBase(); 
  2.             db.Name = "数据库1"
  3.             db.Description = "for test"
  4.             Table tbl = new Table(); 
  5.             tbl.Name = "Student"
  6.             tbl.Description = "学生信息表"
  7.             db.TableList.Add(tbl); 
  8.             Column c1 = new Column(); 
  9.             c1.Name = "StudentName"
  10.             c1.ColumnType = 1; 
  11.             c1.IsNullable = false
  12.             c1.Remark = "姓名"
  13.             Column c2 = new Column(); 
  14.             c2.Name = "Number"
  15.             c2.ColumnType = 1; 
  16.             c2.IsPrimaryKey = true
  17.             c2.Remark = "学号"
  18.             Column c3 = new Column(); 
  19.             c3.Name = "Age"
  20.             c3.ColumnType = 2; 
  21.             c3.Remark = "年龄"
  22.   
  23.             tbl.ColumnList.Add(c1); 
  24.             tbl.ColumnList.Add(c2); 
  25.             tbl.ColumnList.Add(c3); 
  26.              
  27.             //保存数据到XML文件 
  28.         db.SaveToFile("D:\\mytest.xml"); 
  29.   
  30.             //从XML文件中加载数据 
  31.         DataBase db2 = new DataBase(); 
  32.             db2.LoadFromFile("D:\\mytest.xml"); 
总结:
1.在数据对象的定义过程中,我们除了要定义数据本身的属性和方法,还要定义对象本身所对应的XML节点结构,从而使对象能够自我解析XML节点,达到了解耦的目的。
2.数据对象负责解析自己,这算是“职责单一”的设计原则的体现吧?哈哈,后来看了设计模式,这似乎就是传说中的装饰者模式呢。
3.虽然这已经改进了XML文件的存取配置,但依然不算很通用,能不能实现一种更通用甚至是万能的XML配置方法呢?请看下回分解。

本站热点业务

更多模板/案例展示

关于我们 | 联系我们 | 团队日志 | 网站地图 | 网站合作