在前两篇文章中我们讨论了XML文件的读取和写入,但都是基于流模型的解决方案,今天我们就来谈谈在C#中如何实现DOM,DOM确实有它的不足,但在编程工作中它还是不可或缺的技术。下面我们来简单了解一下DOM的相关知识。
DOM的全称是Document Object Model(文档对象模型),它是来自W3C的官方标准,它允许按照W3C标准W3C DOM Level1和W3C DOM Level2的规范所定义的规则,通过编程来读取,操纵和修改XML文档。DOM的工作方式是:首先将XML文档一次性的装入内存,然后根据文档中定义的元素和属性在内存中创建一个“树型结构”也就是一个文档对象模型,这里的含义其实是把文档对象化,文档中每个节点对应着模型中一个对象,而我们都知道对象提供编程接口,所以在Application中我们正是使用这组对象来访问XML文档进而操作XML文档,下图阐述了Application和DOM交互的过程:
DOM既然是在内存中创建树型结构视图进而提供编程接口,那我们就以下面这个XML片段来说明DOM是如何创建树型结构的:
<parent> <child id=”123”>text here</child> </parent>
如果用DOM加载以上文档,它将在内存中创建的树型结构如下图: DOM的关键在于它允许直接更新内存中的树型结构,而不必重定向到其他输出,因此,添加、更新或删除结构中信息的操作效率更高。而作为程序员的我们重要的是要了解DOM所提供的编程接口以实现对XML文档进行操作,事实上,.NET Framework定义了一组类用于反映DOM的体系结构,下面来看一下.NET DOM的继承结构: 在上图中所有弧角矩形中所包含的类描述了所有可能在XML文档中出现的节点类型,而操作XML文档不外乎是操作其中的节点,这些类又都是从XmlNode类派生而来,所以我们今天的主题是讨论XmlNode类和它的子类XmlDocument,下面对这些类做简单的介绍: XmlNode类: 该类是DOM中所有其他节点的抽象基类,它定义所有在更低级的类中继承或重写的成员。它表示XML文档中的单一节点,它提供了用于导航DOM树型结构的基本方法和属性,使用XmlNodeType枚举器可以枚举其下的所有节点类型。以下介绍该类的部分属性和方法: 属性: [C#] public virtual bool HasChildNodes {get;} 获取一个值,该值指示当前节点是否有任何子节点 public virtual XmlNodeList ChildNodes {get;} 获取当前节点的所有子节点 public virtual XmlNode FirstChild {get;} 获取当前节点的第一个子级 public virtual XmlNode LastChild {get;} 获取当前节点的最后一个子级 public virtual XmlNode ParentNode {get;} 获取当前节点的父级 public virtual XmlNode NextSibling {get;} 获取当前节点的下一个兄弟节点 public virtual XmlNode PreviousSibling {get;} 获取当前节点的上一个兄弟节点 public virtual string InnerText {get; set;} 获取或设置当前节点及其所有子节点的文本内容的串联值 public virtual string InnerXml {get; set;} 获取或设置仅代表当前节点的子节点的标记 public virtual string OuterXml {get;} 获取表示当前节点及其所有子节点的标记 方法: public XmlNodeList SelectNodes(string); 选择文档中匹配 XPath 表达式的节点列表 public XmlNode SelectSingleNode(string); 选择文档中匹配 XPath 表达式的第一个 XmlNode public virtual XmlNode AppendChild(XmlNode newChild) 将指定的节点添加到该节点的子节点列表的末尾 public virtual XmlNode PrependChild(XmlNode newChild) 将指定的节点添加到该节点的子节点列表的开头 public virtual XmlNode RemoveChild(XmlNode oldChild) 移除指定的子节点 public virtual XmlNode ReplaceChild(XmlNode newChild,XmlNode oldChild) 用 newChild 节点替换子节点 oldChild XmlNodeList类: 该类表示XmlNode的有序集合,它有以下常用成员: Count——以整数形式返回XmlNodeList中的节点数 ItemOf——搜索在指定索引处的节点 GetEnumerator()——提供迭代遍历节点列表的foreach样式 Item()——返回参数指定的索引处的节点 XmlDocument类: XmlDocument类是XML文档的.NET表示形式,它代表了内存中树型结构的文档节点(所有的节点都在文档节点下),XmlDocument类包含所有的CreateXXX()方法,这些方法允许创建所有派生自XmlNode的类型的节点,通常将该类与XmlNode类一起使用以完成对文档的操作,该类有一个Load()方法用于加载XML文档,该方法的一个重载版本允许从XmlTextReader加载文档,这给我们带来的好处是在操作较大的文档时我们可以先使用XmlTextReader过滤不相关的文档部分,这样即解决了DOM所带来的资源损耗问题又可以保留DOM对文档操控的便利性,该类的Save()方法用于保存文档。 接下来用一个简单的例子来说明在C#中如何实现DOM,照旧看代码前先看下运行效果图: LoadXML按纽用于加载XML文档,LoadXMLReader按纽使用XmlTextReader加载文档,SaveXML按纽保存文档,SaveXMLWriter按纽将文档保存到XmlTextWriter中,Add Product按纽添加节点,Replace Product按纽替换节点,Change Order按纽修改文档,Remove Product Info按纽移除节点。 DomOperation类封装了所有按纽功能的实现,代码如下:>>>点击查看源代码
namespace
DOMSamples
...
{ using System; using System.Xml; using System.Text; using System.Windows.Forms; using System.ComponentModel; /**/ /// <summary> /// DomOperation 提供对Xml文件操作的类 /// </summary> /// <remarks> /// 该类用于提供对XML文件进行一般的操作,如(添加,删除,替换节点,加载,保存文件)等,该类内部实现采用DOM技术。 /// </remarks> public class DomOperation : IDisposable ... { private string _xmlPath; private XmlDocument xmlDoc; DomOperation 构造器 #region DomOperation 构造器 /**/ /// <summary> /// 默认构造器,对该类成员进行默认赋值 /// </summary> public DomOperation() ... { this ._xmlPath = string .Empty; this .xmlDoc = null ; } /**/ /// <summary> /// 带参构造器,对该类成员进行初始赋值 /// </summary> /// <param name="xmlPath"> xml文件路径 </param> public DomOperation( string xmlPath) ... { this ._xmlPath = xmlPath; this .xmlDoc = null ; } #endregion DomOperation 资源释放方法 #region DomOperation 资源释放方法 /**/ /// <summary> /// 清理该对象所有正在使用的资源 /// </summary> public void Dispose() ... { this .Dispose( true ); GC.SuppressFinalize( this ); } /**/ /// <summary> /// 释放该对象的实例变量 /// </summary> /// <param name="disposing"></param> protected virtual void Dispose( bool disposing) ... { if ( ! disposing) return ; if ( this ._xmlPath != null ) ... { this ._xmlPath = null ; } if ( this .xmlDoc != null ) ... { this .xmlDoc = null ; } } #endregion DomOperation 属性 #region DomOperation 属性 /**/ /// <summary> /// 获取或设置Xml文件的路径 /// </summary> public string xmlPath ... { get ... { return _xmlPath; } set ... { this ._xmlPath = value; } } #endregion /**/ /// <summary> /// 加载XML文件 /// </summary> /// <remarks> /// 该方法使用XML文件的路径加载XML文件并返回整个XML文件的内容 /// </remarks> /// <returns> 整个XML文件的内容 </returns> public string Load() ... { xmlDoc = new XmlDocument(); try ... { xmlDoc.Load( this ._xmlPath); } catch (XmlException xmlExp) ... { throw new XmlException(xmlExp.ToString()); } return xmlDoc.OuterXml; } /**/ /// <summary> /// 加载XML文件 /// </summary> /// <remarks> /// 该方法使用XmlReader在XML文档中定位并加载XML文件最后返回XML文件的内容 /// </remarks> /// <param name="nodeType"> 将要定位的节点类型 </param> /// <param name="localName"> 将要定位的节点名称 </param> /// <returns> XML文件的内容 </returns> public string LoadByXmlReader(XmlNodeType nodeType, string localName) ... { string xmlStr = string .Empty; XmlTextReader xmlTxtRd = new XmlTextReader( this ._xmlPath); xmlDoc = new XmlDocument(); try ... { // 在文档中定位 while (xmlTxtRd.Read()) ... { if (xmlTxtRd.NodeType == nodeType) if (xmlTxtRd.LocalName == localName) break ; } xmlDoc.Load(xmlTxtRd); xmlTxtRd.Close(); xmlStr += " ===== OuterXml ===== " ; xmlStr += System.Environment.NewLine; xmlStr += xmlDoc.FirstChild.OuterXml; xmlStr += System.Environment.NewLine; xmlStr += System.Environment.NewLine; xmlStr += " ===== InnerXml ===== " ; xmlStr += System.Environment.NewLine; xmlStr += xmlDoc.FirstChild.InnerXml; xmlStr += System.Environment.NewLine; xmlStr += System.Environment.NewLine; xmlStr += " ===== InnerText ===== " ; xmlStr += System.Environment.NewLine; xmlStr += xmlDoc.FirstChild.InnerText; xmlStr += System.Environment.NewLine; xmlStr += System.Environment.NewLine; xmlStr += " ===== Value ===== " ; xmlStr += System.Environment.NewLine; xmlStr += xmlDoc.FirstChild.ChildNodes[ 0 ].ChildNodes[ 0 ].Value + " " + xmlDoc.FirstChild.ChildNodes[ 1 ].ChildNodes[ 0 ].Value + " " + xmlDoc.FirstChild.ChildNodes[ 2 ].ChildNodes[ 0 ].Value; xmlStr += System.Environment.NewLine; xmlStr += System.Environment.NewLine; } catch (XmlException xmlExp) ... { throw new XmlException(xmlExp.ToString()); } finally ... { if (xmlTxtRd != null && xmlTxtRd.ReadState != ReadState.Closed) xmlTxtRd.Close(); } return xmlStr; } /**/ /// <summary> /// 保存XML文件 /// </summary> /// <remarks> /// 该方法将传入的XML文本保存在传入的路径中最后返回XML文件的内容 /// </remarks> /// <param name="xmlText"> XML文本 </param> /// <param name="savePath"> 保存路径 </param> /// <returns> XML文件的内容 </returns> public string Save( string xmlText, string savePath) ... { xmlDoc = new XmlDocument(); try ... { xmlDoc.LoadXml(xmlText); xmlDoc.Save(savePath); xmlDoc.Load(savePath); } catch (XmlException xmlExp) ... { throw new XmlException(xmlExp.ToString()); } return xmlDoc.OuterXml; } /**/ /// <summary> /// 保存XML文件 /// </summary> /// <remarks> /// 该方法将传入的XML文本保存在XmlTextWriter编写器中并使用传入的路径构造该编写器最后返回XML文件的内容 /// </remarks> /// <param name="xmlText"> XML文本 </param> /// <param name="savePath"> 保存路径 </param> /// <returns> XML文件的内容 </returns> public string SaveByXmlWriter( string xmlText, string savePath) ... { XmlTextWriter xmlTxtWt = new XmlTextWriter(savePath,Encoding.Unicode); xmlDoc = new XmlDocument(); try ... { xmlDoc.LoadXml(xmlText); xmlDoc.Save(xmlTxtWt); xmlTxtWt.Close(); xmlDoc.Load(savePath); } catch (XmlException xmlExp) ... { throw new XmlException(xmlExp.ToString()); } finally ... { if (xmlTxtWt != null && xmlTxtWt.WriteState != WriteState.Closed) xmlTxtWt.Close(); } return xmlDoc.OuterXml; } /**/ /// <summary> /// 添加子节点 /// </summary> /// <remarks> /// 该方法利用传入的父节点路径选择该节点并将传入的XML文档片段添加到该父节点下最后返回添加后XML文件的内容 /// </remarks> /// <param name="xmlDoc"></param> /// <param name="xmlDocFrag"> XML文档片段 </param> /// <param name="parentNodePath"> 父节点路径 </param> /// <returns> 添加后XML文件的内容 </returns> public string AddChildNode(XmlDocument xmlDoc, XmlDocumentFragment xmlDocFrag, string parentNodePath) ... { this .xmlDoc = xmlDoc; XmlElement selectEle = (XmlElement)xmlDoc.SelectSingleNode(parentNodePath); selectEle.AppendChild(xmlDocFrag.FirstChild); return this .xmlDoc.OuterXml; } /**/ /// <summary> /// 替换子节点 /// </summary> /// <remarks> /// 该方法利用父节点路径选择该节点并用传入的XML文档片段替换该父节点下的第i个子节点最后返回替换后的XML文件的内容 /// </remarks> /// <param name="xmlDoc"></param> /// <param name="xmlDocFrag"> XML文档片段 </param> /// <param name="parentNodePath"> 父节点路径 </param> /// <param name="i"> 第i个子节点 </param> /// <returns> 替换后的XML文件的内容 </returns> public string ReplaceChildNode(XmlDocument xmlDoc, XmlDocumentFragment xmlDocFrag, string parentNodePath, int i) ... { this .xmlDoc = xmlDoc; XmlElement selectEle = (XmlElement)xmlDoc.SelectSingleNode(parentNodePath); XmlElement childEle = (XmlElement)selectEle.ChildNodes[i]; selectEle.ReplaceChild(xmlDocFrag.FirstChild,childEle); return this .xmlDoc.OuterXml; } /**/ /// <summary> /// 移除子节点 /// </summary> /// <remarks> /// 该方法利用传入的父节点名称选择该父节点并利用传入的子节点名称选择该子节点选定后使用父节点的RemoveChild方法移除子节点 /// 最后返回移除后XML的文件内容 /// </remarks> /// <param name="parentNodeName"> 父节点名称 </param> /// <param name="childNodeName"> 子节点名称 </param> /// <returns> 移除后XML的文件内容 </returns> public string RemoveChildNode( string parentNodeName, string childNodeName) ... { xmlDoc = new XmlDocument(); xmlDoc.Load( this ._xmlPath); XmlNodeList parentNodeList = xmlDoc.GetElementsByTagName(parentNodeName); foreach (XmlNode parentNode in parentNodeList) ... { XmlNodeList childNodeList = parentNode.SelectNodes(childNodeName); foreach (XmlNode childNode in childNodeList) ... { parentNode.RemoveChild(childNode); } } return xmlDoc.OuterXml; } } }
窗口程序代码如下:
namespace
DOMSamples
...
{ using System; using System.Xml; using System.Text; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; /**/ /// <summary> /// Form1 的摘要说明。 /// </summary> public class Form1 : System.Windows.Forms.Form ... { private System.Windows.Forms.TextBox textBox1; private System.Windows.Forms.Button button1; private System.Windows.Forms.Button button2; private System.Windows.Forms.Button button3; private System.Windows.Forms.Button button4; private System.Windows.Forms.Button button5; private System.Windows.Forms.Button button6; private System.Windows.Forms.Button button7; private System.Windows.Forms.Button button8; private string xmlPath; /**/ /// <summary> /// 必需的设计器变量。 /// </summary> private System.ComponentModel.Container components = null ; public Form1() ... { // // Windows 窗体设计器支持所必需的 // InitializeComponent(); // // TODO: 在 InitializeComponent 调用后添加任何构造函数代码 // } /**/ /// <summary> /// 清理所有正在使用的资源。 /// </summary> protected override void Dispose( bool disposing ) ... { if ( disposing ) ... { if (components != null ) ... { components.Dispose(); } } base .Dispose( disposing ); } Windows 窗体设计器生成的代码 #region Windows 窗体设计器生成的代码 /**/ /// <summary> /// 设计器支持所需的方法 - 不要使用代码编辑器修改 /// 此方法的内容。 /// </summary> private void InitializeComponent() ... { this .textBox1 = new System.Windows.Forms.TextBox(); this .button1 = new System.Windows.Forms.Button(); this .button2 = new System.Windows.Forms.Button(); this .button3 = new System.Windows.Forms.Button(); this .button4 = new System.Windows.Forms.Button(); this .button5 = new System.Windows.Forms.Button(); this .button6 = new System.Windows.Forms.Button(); this .button7 = new System.Windows.Forms.Button(); this .button8 = new System.Windows.Forms.Button(); this .SuspendLayout(); // // textBox1 // this .textBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this .textBox1.Location = new System.Drawing.Point( 8 , 8 ); this .textBox1.Multiline = true ; this .textBox1.Name = " textBox1 " ; this .textBox1.ScrollBars = System.Windows.Forms.ScrollBars.Both; this .textBox1.Size = new System.Drawing.Size( 776 , 296 ); this .textBox1.TabIndex = 0 ; this .textBox1.Text = "" ; // // button1 // this .button1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this .button1.Location = new System.Drawing.Point( 8 , 312 ); this .button1.Name = " button1 " ; this .button1.Size = new System.Drawing.Size( 56 , 23 ); this .button1.TabIndex = 1 ; this .button1.Text = " LoadXML " ; this .button1.Click += new System.EventHandler( this .button1_Click); // // button2 // this .button2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this .button2.Location = new System.Drawing.Point( 72 , 312 ); this .button2.Name = " button2 " ; this .button2.Size = new System.Drawing.Size( 96 , 23 ); this .button2.TabIndex = 2 ; this .button2.Text = " LoadXMLReader " ; this .button2.Click += new System.EventHandler( this .button2_Click); // // button3 // this .button3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this .button3.Location = new System.Drawing.Point( 176 , 312 ); this .button3.Name = " button3 " ; this .button3.Size = new System.Drawing.Size( 56 , 23 ); this .button3.TabIndex = 3 ; this .button3.Text = " SaveXML " ; this .button3.Click += new System.EventHandler( this .button3_Click); // // button4 // this .button4.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this .button4.Location = new System.Drawing.Point( 240 , 312 ); this .button4.Name = " button4 " ; this .button4.Size = new System.Drawing.Size( 96 , 23 ); this .button4.TabIndex = 4 ; this .button4.Text = " SaveXMLWriter " ; this .button4.Click += new System.EventHandler( this .button4_Click); // // button5 // this .button5.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this .button5.Location = new System.Drawing.Point( 344 , 312 ); this .button5.Name = " button5 " ; this .button5.Size = new System.Drawing.Size( 80 , 23 ); this .button5.TabIndex = 5 ; this .button5.Text = " Add Product " ; this .button5.Click += new System.EventHandler( this .button5_Click); // // button6 // this .button6.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this .button6.Location = new System.Drawing.Point( 432 , 312 ); this .button6.Name = " button6 " ; this .button6.Size = new System.Drawing.Size( 112 , 23 ); this .button6.TabIndex = 6 ; this .button6.Text = " Replace Product " ; this .button6.Click += new System.EventHandler( this .button6_Click); // // button7 // this .button7.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this .button7.Location = new System.Drawing.Point( 552 , 312 ); this .button7.Name = " button7 " ; this .button7.Size = new System.Drawing.Size( 88 , 23 ); this .button7.TabIndex = 7 ; this .button7.Text = " Change Order " ; this .button7.Click += new System.EventHandler( this .button7_Click); // // button8 // this .button8.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this .button8.Location = new System.Drawing.Point( 648 , 312 ); this .button8.Name = " button8 " ;