文档对象模型操作xml文档
简介 :文档对象模型(DOM)是一种用于处理xml文档的API函数集。
2.1文档对象模型概述
按照W3C的定义,DOM是“一种允许程序或脚本动态地访问更新文档内容,结构和样式的、独立于平台和语言的规范化接口。DOM是表示文档(比如HTML和XML)和访问、操作构成文档的各种元素的应用程序接口,它以树状结构表示HTML和XML文档,定义了遍历这个树和检查、修改树的节点的方法和属性。
DOM的核心API还允许你创建和填充文件、加载文档并保存。
2.2DOM实现
微软的net框架在Systemx.xml命名空间提供了一系列的类用于DOM实现,xmlDocument是NET中Dom实现的核心类之一。正如其他的DOM解析器一样,该类是NET框架的DOC解析器。
xmlDocument将XML文档视为树状结构,他装在xml文档并在内存中构建该文档的树状结构。XmlDocument类代表了一个xml文档、,它支持xml的增删改查;
xmlNode代表一个节点。
xml文档组成 部分 | 对应的类 |
Document Element(文档元素) | XmlElement |
Processing Instructions(处理指令) | XmlProcessingIntruction |
Element(元素) | XmlElement |
Attribute(属性) | XmlAttribute |
Text Values(文本值) | XmlText |
Nodes(节点) | XmlNode |
表中所提及的类都直接或者间接的继承了抽象类的XmlNode。
2.3应用实例
2.3.1装载xml文档
XmlDocument类允许你通过三种方式打开一个xml文档:
- 指定xml文档路径路程或者URL
- 包含xml文档数据的文件流对象
- 包含xml文档数据的字符串
接来用这三种方法尝试打开xml文档。代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void btnopen_Click(object sender, EventArgs e) { try { XmlDocument doc = new XmlDocument(); if (rdbURL.Checked) { doc.Load(txtpath.Text); } if (rdbstream.Checked) { FileStream stream = new FileStream(txtpath.Text,FileMode.Open); doc.Load(stream); stream.Close(); } if (rdbstring.Checked) { doc.LoadXml(txtpath.Text);//加载报错,Loading从指定的字符串中加载xml文档。 doc.LoadXml("<customer ><firstname>Ernestine</firstname><lastname>Borrison</lastname><homephone>(445) 269-7742</homephone><notes>123</notes></customer>"); // } MessageBox.Show("XML Document Opened Successfully!"); } catch(Exception ex) { MessageBox.Show(ex.Message); } } }
xmlDocument类具有俩个重要的方法:Load()和LoadXml()用于加载文档。Load方法通过指定xml文档的文件路径、URL或指向XML文档的流对象来打开XML 文档。
LoadXml方法中则通过指定包含xml文档的字符串来打开xml文档。在这个例子中,LoadXml加载的是文件的路径,导致加载无法成功,讲字符串替换为含有根节点的字符串即可,这个字符串中的节点并不一定是xml文档的根节点,是你选取的这个字符串中的根节点,其他节点应该包含在这个节点的内部,不能含有多个同等级的节点。
2.3.2遍历xml文档
一个xml文档可以包含一个或者对个节点,而每一个节点又可以包含多个子节点,xmlnode类具有一个叫Childnoes的集合体,该集合体包好某种条件下的所有子节点。net框架中与DOM 相关联的其他许多类都直接或者间接的继承自XmlNode类。因而这些类可以调用childNodes集合体。
例子代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
private void btnload_Click(object sender, EventArgs e) { XmlDocument _doc = new XmlDocument();//创建实例 _doc.Load(Application.StartupPath + "/Customers.xml");//加载文件 TreeNode _root = new TreeNode(_doc.DocumentElement.Name);//documentElement 属性可返回文档的根节点 treeView1.Nodes.Add(_root); foreach (XmlNode node in _doc.DocumentElement.ChildNodes) { TreeNode _customer = new TreeNode("Customer ID : " + node.Attributes["customerid"].Value);//Attributes表示获取对应的属性信息 _root.Nodes.Add(_customer); if (node.HasChildNodes) { foreach (XmlNode childnode in node.ChildNodes) { TreeNode _customer2 = new TreeNode(childnode.Name + " : " + childnode.InnerText);//InnerText获取节点的内部数据 。。。 _customer.Nodes.Add(_customer2); } } } }
2.3.3查询特殊元素和节点
在实际的使用中我们使用下面这几种方法来查询xml文档树中的 某个或某些元素和节点,去获得相关的信息和数据值。
- Ge他ElementByTagName()方法
- GetElementById()方法
- SelectNodes()方法
- SelectSingleNode()方法
1、XmlDcument类的GetElementByTagName()方法以节点的标签名为输入参数。并返回所有具有相同标签名的节点。这些节点包含在一个XmlNodeList类的实例中。
XmlNodeList类代表一个XmlNode对象的集合。代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public partial class Form1 : Form { #region Constructor public Form1() { InitializeComponent(); } #endregion #region Variables private XmlNodeList list = null;//代表一个XmlNode对象的集合、 #endregion private void btnsearch_Click(object sender, EventArgs e) { lstresult.Items.Clear(); XmlDocument _doc = new XmlDocument(); _doc.Load(Application.StartupPath + "/customers.xml"); list = _doc.GetElementsByTagName(txttag.Text);//通过标签名获取list foreach (XmlNode node in list) { lstresult.Items.Add(node.Name); } } private void lstresult_SelectedIndexChanged(object sender, EventArgs e) { txtresult.Text = list[lstresult.SelectedIndex].InnerText; }
2、应用GetElementByld方法
如果xml文档中存在一个具有唯一值的属性,比如人民的身份证这个属性。在查找特殊元素或者节点时,可应用GetElementByld()方法加以实现,其实现方式类似于应用主键在数据库中
查询相应的记录。问题在XmlDocument类不能自动的指定元素的某个特殊的属性作为元素的主键,因此在应用这个方法前,必须在xml文档中通过DTD或者Schema技术指定元素的某个属性作为元素的唯一的“主键”,同时使XmlDocument类能够将该属性视为元素的“主键”。
声明主键的代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE customers[ <!ELEMENT customers ANY> <!ELEMENT customer ANY> <!ELEMENT firstname ANY> <!ELEMENT lastname ANY> <!ELEMENT homephone ANY> <!ELEMENT notes ANY> <!ATTLIST customer customerid ID #REQUIRED> ]>
注意这行代码<!ATTLIST customer customerid ID #REQUIRED>,在此我们将属性customer id 标记为ID并同时规定其实唯一的(#REQUIRED)
这个方法的返回值是包含相应节点数据的XmlElement类的实例对象。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
private void Form1_Load(object sender, EventArgs e) { doc = new XmlDocument(); doc.Load(Application.StartupPath+"/Customers.xml"); foreach(XmlNode node in doc.DocumentElement.ChildNodes) { string _strid=node.Attributes["customerid"].Value; cmbID.Items.Add(_strid); } } private void cmbID_SelectedIndexChanged(object sender, EventArgs e) { XmlElement _xel = doc.GetElementById(cmbID.SelectedItem.ToString()); lblfirst.Text = _xel.ChildNodes[0].InnerText; lbllast.Text = _xel.ChildNodes[1].InnerText; lblphone.Text = _xel.ChildNodes[2].InnerText; lblnote.Text = _xel.ChildNodes[3].InnerText; }
3、应用SelectNodes()方法
在某些情况下,我们需要查询xml文档中付某中或者某些条件的节点。SelectNodes()可以满足这种要求,该方法可以根据查询条件过滤得到符合条件的节点。
返回值一个包含所有有效节点得到XmlNodeList实例对象。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public partial class Form1 : Form { #region ariables private XmlNodeList list = null; #endregion #region Constructor public Form1() { InitializeComponent(); } #endregion private void btnsearch_Click(object sender, EventArgs e) { lstresult.Items.Clear(); XmlDocument _doc = new XmlDocument(); _doc.Load(Application.StartupPath + "/Customers.xml"); if (rdbfirst.Checked) { //string str = string.Format("//customer[./firstname/text()='{0}']", txtinput.Text); // string str = string.Format("//customer[./homephone[./quhao/text()='{0}']]", txtinput.Text); //////customer[./firstname/text()='John'] list = _doc.SelectNodes(str); } if (rdblast.Checked) { list = _doc.SelectNodes(string.Format("//customer[./lastname/text()='{0}']", txtinput.Text)); } foreach (XmlNode node in list) { lstresult.Items.Add(node.Attributes["customerid"].Value); } } private void btndetail_Click(object sender, EventArgs e) { if (lstresult.SelectedIndex < 0) { MessageBox.Show("Please Selected a Customer ID"); } else { lblfirst.Text = list[lstresult.SelectedIndex].ChildNodes[0].InnerText; lbllast.Text = list[lstresult.SelectedIndex].ChildNodes[1].InnerText; lblphone.Text = list[lstresult.SelectedIndex].ChildNodes[2].InnerText; lblnote.Text = list[lstresult.SelectedIndex].ChildNodes[3].InnerText; } }
4、应用selectSingleNode()方法
该刚方法的不同于selectNodes的是:方法仅返回符合条件的第一个节点。
2.4修改XML文档
对XML文档的修改包括添加或者插入新节点,删除已存在节点、次该节点的相关数据或者属性。DOM 是一个读写型的解释器,因此dom也有提供许多函数方法和类允许你修改文档。
2.4.1 Save方法
save方法保存文件到指定的位置。 该方法传入的参数为XmlWriter、XmlTextWriter或者字符串。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
string filename=@“C:\books.xml”; XmlDocument xmlDoc= new XmlDocument(); xmlDoc.Load(filename); XmlTextWriter writer = new XmlTextWriter("c:\\domtest.xml",null); writer.Formatting=Formatting.Indented; xmlDoc.Save(writer); //你也可以使用一个文件名或Console.Out保存文档,或者将文档的内容输出到屏幕上 xmlDoc.Save("c:\\domtest.xml"); xmlDoc.Save(Console.Out);
2.4.2 XmlDocumentFragment类
在xml 文档中插入部分内容或者节点时,用到这个类=》这个类自XmlNode派生。=>通过xml文档的CreateDocumentFragment()方法来创建这个类的实例。
该实例的InnerXml属性代表当前节点的子节点。
XmlDocumentFragment docFrag=xmlDoc.CreateDocumentFragment();
2.4.3 XmlElement类
XmlElement代表文档中的一个元素,这个类继承自XmlLinkedNode,XmlLinkedNode类继承自XmlNode。XmlLinkedNode有俩个属性:NextSibing和PreviousSibling、代表与当前节点处于同一个层次的下一个和以前的节点。
下面网址链接此类的常用方法:网址链接
2.4.4 添加节点到XML 文档中
AppendChild()方法添加节点到文档中,方法接受一个XmlNode类类型的单个参数。xmldocument的creatxxx方法可以创建不同的节点,AppendChild可以将他们加到文档中。
添加评论节点代码:xmlDoc.AppendChild(nodel);
添加元素节点到文档中:xmlDoc.DocumentElement.AppendChild(nodel);
2.4.5 删除和更换节点
XmlNode类的RemoveAll()方法可以删除所有元素和节点的属性、方法RemoveChild()仅用于删除指定的子节点。
代码:XmlNode root= xmlDoc.DocumentElement;root.RemoveALll();
ReplaceChild()方法用于一个新的节点替换旧的节点rootNode.ReplaceChild(xmlDocFragment,rootNode.LastChild);
2.4.6 XML片段插入到xml文档中
XmlNode类提供相应的方法将xml片段插入到xml文档中,例如:InsertAfter()方法在当前节点之后插入一个文档或者元素。
该方法需要俩个参数,1:XmlDocumentFragment对象,2:要在其中插入片段的位置。(插入位置)
aNode.InsertAfter(xmlDocFragment,aNode.LastChild);
2.4.7添加属性到节点中
使用XmlElement类的SetAttributeNode()方法添加节点的属性。
XmlElement newElem=xmlDoc.CreateElement("NewElement"); //创建指定名称的元素 XmlAttribute newAttr=xmlDoc.CreateAttribute("NewAttribute"); newElem.SetAttributeNode(newAttr);
2.5综合实例
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public partial class Form1 : Form { #region Variables private XmlDocument doc; private int nodeindex = 0; private bool isadd = false; #endregion #region Constructor public Form1() { InitializeComponent(); } #endregion #region Methods private void AddItemsIntoCombBox(XmlDocument _doc) { cmbID.Items.Clear(); foreach (XmlNode _node in _doc.DocumentElement.ChildNodes) { cmbID.Items.Add(_node.Attributes["customerid"].Value); } } private void NodeRemoved(object sender, XmlNodeChangedEventArgs e) { MessageBox.Show("Node " + e.Node.Name + " removed successfully!"); } private void NodeInserted(object sender, XmlNodeChangedEventArgs e) { if (isadd) { MessageBox.Show("Node " + e.Node.Name + " added successfully!"); } isadd = false; } private void NodeChanged(object sender, XmlNodeChangedEventArgs e) { MessageBox.Show("Node " + e.Node.Name + " changed successfully!"); } private void FillControlters() { XmlNode _nod = doc.DocumentElement.ChildNodes[nodeindex]; cmbID.Text = _nod.Attributes["customerid"].Value; txtfname.Text = _nod.ChildNodes[0].InnerText; txtlname.Text = _nod.ChildNodes[1].InnerText; txtphone.Text = _nod.ChildNodes[2].InnerText; txtnote.Text = _nod.ChildNodes[3].InnerText; this.UpdateInformation(); } private void UpdateInformation() { lblinformation.Text = "Customer " + (nodeindex + 1) + " of " + doc.DocumentElement.ChildNodes.Count.ToString(); } #endregion private void Form1_Load(object sender, EventArgs e) { doc = new XmlDocument(); doc.Load(Application.StartupPath + "/customers.xml"); this.AddItemsIntoCombBox(doc); this.FillControlters(); doc.NodeChanged += new XmlNodeChangedEventHandler(NodeChanged); doc.NodeInserted += new XmlNodeChangedEventHandler(NodeInserted); doc.NodeRemoved += new XmlNodeChangedEventHandler(NodeRemoved); } private void btnadd_Click(object sender, EventArgs e) { if ((txtfname.Text == "") || (txtlname.Text == "") || (txtphone.Text == "") || (txtnote.Text == "")) { MessageBox.Show("Please fill up all items of customer!"); return; } XmlElement _customer = doc.CreateElement("customer"); XmlElement _firstname = doc.CreateElement("firstname"); XmlElement _lastname = doc.CreateElement("lastname"); XmlElement _homephone = doc.CreateElement("homephone"); XmlElement _notes = doc.CreateElement("notes"); XmlAttribute _customerid = doc.CreateAttribute("customerid"); _customerid.Value = cmbID.Text; XmlText _firstnametext = doc.CreateTextNode(txtfname.Text); XmlText _lastnametext = doc.CreateTextNode(txtlname.Text); XmlText _homephonetext = doc.CreateTextNode(txtphone.Text); XmlCDataSection _notestext = doc.CreateCDataSection(txtnote.Text); _customer.Attributes.Append(_customerid); _customer.AppendChild(_firstname); _customer.AppendChild(_lastname); _customer.AppendChild(_homephone); _customer.AppendChild(_notes); _firstname.AppendChild(_firstnametext); _lastname.AppendChild(_lastnametext); _homephone.AppendChild(_homephonetext); _notes.AppendChild(_notestext); isadd = true; doc.DocumentElement.AppendChild(_customer); doc.Save(Application.StartupPath + "/Customers.xml"); this.AddItemsIntoCombBox(doc); this.UpdateInformation(); } private void btnupdate_Click(object sender, EventArgs e) { if ((txtfname.Text == "") || (txtlname.Text == "") || (txtphone.Text == "") || (txtnote.Text == "")) { MessageBox.Show("Please fill up all items of customer!"); return; } XmlNode _node = doc.SelectSingleNode("//customer[@customerid='" + cmbID.SelectedItem + "']"); if (_node != null) { if (_node.ChildNodes[0].InnerText != txtfname.Text) _node.ChildNodes[0].InnerText = txtfname.Text; if (_node.ChildNodes[1].InnerText != txtlname.Text) _node.ChildNodes[1].InnerText = txtlname.Text; if (_node.ChildNodes[2].InnerText != txtphone.Text) _node.ChildNodes[2].InnerText = txtphone.Text; if (_node.ChildNodes[3].InnerText != txtnote.Text) { XmlCDataSection _notes = doc.CreateCDataSection(txtnote.Text); isadd = true; _node.ChildNodes[3].ReplaceChild(_notes, _node.ChildNodes[3].ChildNodes[0]); } } doc.Save(Application.StartupPath + "/Customers.xml"); } private void btndelete_Click(object sender, EventArgs e) { XmlNode _node = doc.SelectSingleNode("//customer[@customerid='" + cmbID.SelectedItem + "']"); if (_node != null) { doc.DocumentElement.RemoveChild(_node); } doc.Save(Application.StartupPath + "/Customers.xml"); nodeindex = 0; this.FillControlters(); this.UpdateInformation(); this.AddItemsIntoCombBox(doc); } private void btnfirst_Click(object sender, EventArgs e) { nodeindex = 0; this.FillControlters(); } private void btnprevious_Click(object sender, EventArgs e) { nodeindex--; if (nodeindex < 0) { nodeindex = 0; } this.FillControlters(); } private void btnnext_Click(object sender, EventArgs e) { nodeindex++; if (nodeindex >= doc.DocumentElement.ChildNodes.Count) { nodeindex = doc.DocumentElement.ChildNodes.Count - 1; } this.FillControlters(); } private void btnlast_Click(object sender, EventArgs e) { nodeindex = doc.DocumentElement.ChildNodes.Count - 1; this.FillControlters(); }
2.6处理空白
加载文档时,默认情况下,XMLDocument类忽略空白。通过设置PreserveWhiteSpace的布尔型属性来控制是否需要空白内容,true将保存空白内容,false将不保留空白内容。
2.7XmlDocument类的事件
当修改xml 文档时,会激发小毛驴document类提供的事件过程,这些事件过程分别遵循事前或者事后激发模式。
![]() |
NodeChanged |
当属于该文档的节点的 Value 已被更改时发生。 |
![]() |
NodeChanging |
当属于该文档的节点的 Value 将被更改时发生。 |
![]() |
NodeInserted |
当属于该文档的节点已被插入另一个节点时发生。 |
![]() |
NodeInserting |
当属于该文档的节点将被插入另一个节点时发生。 |
![]() |
NodeRemoved |
Occurs when a node belonging to this document has been removed from its parent. |
![]() |
NodeRemoving |
当属于该文档的节点将从文档中移除时发生。 |
表中的事件都接受一个XmlNodeChangeEventArgs类型的参数
下面提供这个类型参数的一些属性
![]() |
Action |
获取一个值,该值指示正在发生哪种类型的节点更改事件。 |
![]() |
NewParent |
获取操作完成后 ParentNode 的值。 |
![]() |
NewValue |
获取节点的新值。 |
![]() |
Node |
获取正被添加、移除或更改的 XmlNode。 |
![]() |
OldParent |
获取操作开始前的 ParentNode 的值。 |
![]() |
OldValue |
获取节点的原始值。 |