Dom访问Xml笔记
在程序中访问进而操作XML文件一般有两种模型,分别是使用DOM(文档对象模型)和流模型。下面是我的学习笔记,主要是学习怎样用Dom访问Xml。
以下是用到的xml文件,名为contack.xml(其实是写错了,是contact.xml)
<?xmlversion="1.0"encoding="utf-8"?>
<xmltt>
<ContactDetails>
<Contact>
<Name>
<First>luo</First>
<Last>luo</Last>
</Name>
<Note>luoandluo</Note>
</Contact>
</ContactDetails>
</xmltt>
DOM的好处在于它允许编辑和更新XML文档,可以随机访问文档中的数据,可以使用XPath查询,但是,DOM的缺点在于它需要一次性的加载整个文档到内存中,对于大型的文档,这会造成资源问题。流模型很好的解决了这个问题,因为它对XML文件的访问采用的是流的概念,也就是说,任何时候在内存中只有当前节点,但它也有它的不足,它是只读的,仅向前的,不能在文档中执行向后导航操作。(不好意思,都是抄的:),说理解吗,也是有的。
string strXmlPath;
strXmlPath = Server.MapPath("Contack.xml");
XmlDocument xDoc = new XmlDocument();
xDoc.Load(strXmlPath);
XmlElement xEle = null;
//首先查找是否已经存在要添加
xEle = (XmlElement)xDoc.SelectSingleNode(string.Format("//Name[First='{0}' and Last='{1}']",FirstName.Text,LastName.Text));
if (xEle!=null) //已经存在
{
Label3.Text = "already exist";
}
else //不存在,可以添加
{
XmlDocumentFragment xDocFrag = xDoc.CreateDocumentFragment();
XmlElement xContact = xDoc.CreateElement("Contact");
XmlElement xName = xDoc.CreateElement("Name");
XmlElement xFirst = xDoc.CreateElement("First");
xFirst.InnerText = FirstName.Text;
XmlElement xLast = xDoc.CreateElement("Last");
xLast.InnerText = LastName.Text;
xName.AppendChild(xFirst);
xName.AppendChild(xLast);
XmlElement xNote = xDoc.CreateElement("Note");
xNote.InnerText = FirstName.Text+"and"+LastName.Text;
xContact.AppendChild(xName);
xContact.AppendChild(xNote);
xDocFrag.AppendChild(xContact);
XmlNode xRoot = null;
xRoot = xDoc.SelectSingleNode("//ContactDetails"); //ContactDetails
if (xRoot!=null)
{
//xRoot.AppendChild(xDocFrag.FirstChild);// 加在最后
xRoot.PrependChild(xDocFrag.FirstChild); //加在最前,做为第一个子节点
//还有InsertBefore,InsertAfter(new,old)
xDoc.Save(strXmlPath);
Label3.Text = "add sucess"; //
}
else
{
Label3.Text = "add failed";
}
string str = Server.MapPath("Contack.xml");
XmlDocument xDoc = new XmlDocument();
xDoc.Load(str);
XmlElement xEle = null;
//查看要修改的元素是否存在
xEle = (XmlElement)xDoc.SelectSingleNode(string.Format("//Name[First='{0}' and Last='{1}']",FirstName.Text,LastName.Text));
if (xEle==null) //不存在
{
Label5.Text = "no exist";
}
else //存在
{
//下面是一种方法
/*xEle.ChildNodes[0].InnerXml = FirstName.Text + "Replace";
xEle.ChildNodes[1].InnerXml = LastName.Text + "Replace";
xEle.NextSibling.InnerXml = FirstName.Text + "and" + LastName.Text + "Replace";*/
//也可以这样
//true表示递归地克隆指定节点下的子树
XmlElement xNodeCopy = (XmlElement)xEle.CloneNode(true);//
xNodeCopy.ChildNodes[0].InnerXml = FirstName.Text + "Replace";
xNodeCopy.ChildNodes[1].InnerXml = LastName.Text + "Replace";
//xNodeCopy.NextSibling.InnerXml = FirstName.Text + "and" + LastName.Text + "Replace";
//xNodeCopy.ParentNode.ReplaceChild(xNodeCopy,xEle); //这样是错误的,因为xNodeCopy只是clone了这个点及子点,并没有父点
xEle.ParentNode.ReplaceChild(xNodeCopy,xEle);
}
xDoc.Save(str);
}
string str;
str = Server.MapPath("Contack.xml");
XmlDocument xDoc = new XmlDocument();
xDoc.Load(str);
XmlElement xEle = null;
//查找是否有存在指定的点
xEle = (XmlElement)xDoc.SelectSingleNode(string.Format("//Name[First='{0}' and Last='{1}']",FirstName.Text,LastName.Text));
if (xEle!=null) //存在
{
xEle.ParentNode.ParentNode.RemoveChild(xEle.ParentNode);
Label6.Text = "delete sucess";
}
else
{
Label6.Text = "no find";
}
xDoc.Save(str);
string strSrc,strDst;
strSrc = Server.MapPath("Contack.xml"); //源文件
strDst = Server.MapPath("Dst.xml"); //目标文件
XmlDocument xSrcDoc = new XmlDocument();
xSrcDoc.Load(strSrc);
XmlDocument xDstDoc = new XmlDocument();
xDstDoc.Load(strDst);
XmlNodeList xList = null;
XmlNode xDstNode = null;
//假定目标xml中已经存在ContactDetails元素
xDstNode = xDstDoc.SelectSingleNode("//ContactDetails");
xList = xSrcDoc.SelectNodes("//Contact"); //选择所有的点
if (xList!=null&&xDstNode!=null)
{
foreach (XmlNode xNode in xList)
{
XmlNode xNodeImp = xDstDoc.ImportNode(xNode,true); //不同xml的 node需导入后才可以在自己的xml 中用
xDstNode.AppendChild(xNodeImp);
}
}
xDstDoc.Save(strDst);
string strDst;
strDst = Server.MapPath("Dst.xml");
XmlDocument xSrcDoc = new XmlDocument();
xSrcDoc.Load(strDst);
//xSrcDoc.DocumentElement.FirstChild.RemoveAll(); //还剩下头节点
xSrcDoc.DocumentElement.RemoveAll(); //删除全部节点
xSrcDoc.Save(strDst);
由PrependChild, AppendChild,ReplaceChild, RemoveChild, AppendChild函数可知道,他们的操作都是 父元素.成员函数(操作),所以一般要先找到父元素,Node.ParentNode可以找到
仔细看看用到的类,很多都是直接或间接从XmlNode派生的
//Name
Contack.xml中所有的Name元素
//Name[FirstName='flypig' and LastName='luo']
Contack.xml中FirstName='flypig' 并且 LastName='luo'的Name元素
//cd[@country]
选择所有含有country这个属性的cd元素.
//cd[@*]
选择出含有属性的所有cd元素
//cd[@country='UK']
选择出country属性值为UK的cd元素
XPath 是XML的查询语言,和SQL的角色很类似。以下面XML为例,介绍XPath 的语法。
<?xml version="1.0" encoding="ISO-8859-1"?>
<catalog>
<cd country="USA">
<title>Empire Burlesque</title>
<artist>Bob Dylan</artist>
<price>10.90</price>
</cd>
<cd country="UK">
<title>Hide your heart</title>
<artist>Bonnie Tyler</artist>
<price>9.90</price>
</cd>
<cd country="USA">
<title>Greatest Hits</title>
<artist>Dolly Parton</artist>
<price>9.90</price>
</cd>
</catalog>
定位节点
XML是树状结构,类似档案系统内资料夹的结构,XPath也类似档案系统的路径命名方式。不过XPath 是一种模式(Pattern),可以选出 XML档案中,路径符合某个模式的所有节点出来。例如要选catalog底下的cd中所有price元素可以用:
/catalog/cd/price
如果XPath的开头是一个斜线(/)代表这是绝对路径。如果开头是两个斜线(//)表示文件中所有符合模式的元素都会被选出来,即使是处于树中不同的层级也会被选出来。以下的语法会选出文件中所有叫做cd的元素(在树中的任何层级都会被选出来):
//cd
选择未知的元素
使用星号(Wildcards,*)可以选择未知的元素。下面这个语法会选出/catalog/cd 的所有子元素:
/catalog/cd/*
以下的语法会选出所有catalog的子元素中,包含有price作为子元素的元素。
/catalog/*/price
以下的语法会选出有两层父节点,叫做price的所有元素。
/*/*/price
以下的语法会选择出文件中的所有元素。
//*
要注意的是,想要存取不分层级的元素,XPath语法必须以两个斜线开头(//),想要存取未知元素才用星号(*),星号只能代表未知名称的元素,不能代表未知层级的元素。
选择分支
使用中括号可以选择分支。以下的语法从catalog的子元素中取出第一个叫做cd的元素。XPath的定义中没有第0元素这种东西。
/catalog/cd[1]
以下语法选择catalog中的最后一个cd元素:(XPathj并没有定义 first() 这种函式喔,用上例的 [1]就可以取出第一个元素。
/catalog/cd[last()]
以下语法选出含有price子元素的所有/catalog/cd元素。
/catalog/cd[price]
以下语法选出price元素的值等于10.90的所有/catalog/cd元素
/catalog/cd[price=10.90]
以下语法选出price元素的值等于10.90的所有/catalog/cd元素 的price元素
/catalog/cd[price=10.90]/price
选择一个以上的路径
使用Or操作数(|)就可以选择一个以上的路径。例如:
/catalog/cd/title | catalog/cd/artist
选择所有title以及artist元素
//title | //artist
选择所有title以及artist以及price元素
//title | //artist | //price
选择属性
在XPath中,除了选择元素以外,也可以选择属性。属性都是以@开头。例如选择文件中所有叫做country的属性:
//@country
选择所有含有country这个属性的cd元素:
//cd[@country]
以下语法选择出含有属性的所有cd元素
//cd[@*]
以下语法选择出country属性值为UK的cd元素
//cd[@country='UK']
节点匹配路径Xpath
之所以要引入XPath的概念,目的就是为了在匹配XML文档结构树时能够准确地找到某一个节点元素。可以把XPath比作文件管理路径:通过文件管理路径,可以按照一定的规则查找到所需要的文件;同样,依据XPath所制定的规则,也可以很方便地找到XML结构文档树中的任何一个节点。
在介绍XPath的匹配规则之前,我们先来看一些有关XPath的基本概念。首先要说的是XPath数据类型。XPath可分为四种数据类型:
节点集(node-set)
节点集是通过路径匹配返回的符合条件的一组节点的集合。其它类型的数据不能转换为节点集。
布尔值(boolean)
由函数或布尔表达式返回的条件匹配值,与一般语言中的布尔值相同,有true和false两个值。布尔值可以和数值类型、字符串类型相互转换。
字符串(string)
字符串即包含一系列字符的集合,XPath中提供了一系列的字符串函数。字符串可与数值类型、布尔值类型的数据相互转换。
根节点(Root Node)
根节点是一棵树的最上层,根节点是唯一的。树上其它所有元素节点都是它的子节点或后代节点。对根节点的处理机制与其它节点相同。在XSLT中对树的匹配总是先从根节点开始。
元素节点(Element Nodes)
元素节点对应于文档中的每一个元素,一个元素节点的子节点可以是元素节点、注释节点、处理指令节点和文本节点。可以为元素节点定义一个唯一的标识id。
元素节点都可以有扩展名,它是由两部分组成的:一部分是命名空间URI,另一部分是本地的命名。
文本节点(Text Nodes)
文本节点包含了一组字符数据,即CDATA中包含的字符。任何一个文本节点都不会有紧邻的兄弟文本节点,而且文本节点没有扩展名。
属性节点(Attribute Nodes)
每一个元素节点有一个相关联的属性节点集合,元素是每个属性节点的父节点,但属性节点却不是其父元素的子节点。这就是说,通过查找元素的子节点可以匹配出元素的属性节点,但反过来不成立,只是单向的。再有,元素的属性节点没有共享性,也就是说不同的元素节点不共有同一个属性节点。
对缺省属性的处理等同于定义了的属性。如果一个属性是在DTD声明的,但声明为#IMPLIED,而该属性没有在元素中定义,则该元素的属性节点集中不包含该属性。
此外,与属性相对应的属性节点都没有命名空间的声明。命名空间属性对应着另一种类型的节点。
命名空间节点(Namespace Nodes)
每一个元素节点都有一个相关的命名空间节点集。在XML文档中,命名空间是通过保留属性声明的,因此,在XPath中,该类节点与属性节点极为相似,它们与父元素之间的关系是单向的,并且不具有共享性。
处理指令节点(Processing Instruction Nodes)
处理指令节点对应于XML文档中的每一条处理指令。它也有扩展名,扩展名的本地命名指向处理对象,而命名空间部分为空。
路径匹配
路径匹配与文件路径的表示相仿,比较好理解。有以下几个符号:
符 号 | 含 义 | 举 例 | 匹配结果 |
/ | 指示节点路径 | /A/C/D | 节点"A"的子节点"C"的子节点"D",即id值为d2的D节点 |
/ | 根节点 | ||
// | 所有路径以"//"后指定的子路径结尾的元素 | //E | 所有E元素,结果是所有三个E元素 |
//C/E | 所有父节点为C的E元素,结果是id值为e1和e2的两个E元素 | ||
* | 路径的通配符 | /A/B/C/* | A元素→B元素→C元素下的所有子元素,即name值为b的B元素、id值为d1的D元素和id值为e1和e2的两个E元素 |
/*/*/D | 上面有两级节点的D元素,匹配结果是id值为d2的D元素 | ||
//* | 所有的元素 | ||
| | 逻辑或 | //B | //C | 所有B元素和C元素 |
位置匹配
对于每一个元素,它的各个子元素是有序的。如:
举 例 | 含 义 | 匹配结果 |
/A/B/C[1] | A元素→B元素→C元素的第一个子元素 | name值为b的B元素 |
/A/B/C[last()] | A元素→B元素→C元素的最后一个子元素 | id值为e2的E元素 |
/A/B/C[position()>1] | A元素→B元素→C元素之下的位置号大于1的元素 | id值为d1的D元素和两个具有id值的E元素 |
属性及属性值
在XPath中可以利用属性及属性值来匹配元素,要注意的是,元素的属性名前要有"@"前缀。例如:
举 例 | 含 义 | 匹配结果 |
//B[@id] | 所有具有属性id的B元素 | id值为b1和b2的两个B元素 |
//B[@*] | 所有具有属性的B元素 | 两个具有id属性的B元素和一个具有name属性B元素 |
//B[not(@*)] | 所有不具有属性的B元素 | A元素→C元素下的B元素 |
//B[@id="b1"] | id值为b1的B元素 | A元素下的B元素 |
亲属关系匹配
XML文档可归结为树型结构,因此任何一个节点都不是孤立的。通常我们把节点之间的归属关系归结为一种亲属关系,如父亲、孩子、祖先、后代、兄弟等等。在对元素进行匹配时,同样可以用到这些概念。例如:
举 例 | 含 义 | 匹配结果 |
//E/parent::* | 所有E节点的父节点元素 | id值为a1的A元素和id值为c1的C元素 |
//F/ancestor::* | 所有F元素的祖先节点元素 | id值为a1的A元素和id值为c2的C元素 |
/A/child::* | A的子元素 | id值为b1、b2的B元素,id值为c2的C元素,以及没有任何属性的E元素 |
/A/descendant::* | A的所有后代元素 | 除A元素以外的所有其它元素 |
//F/self::* | 所有F的自身元素 | F元素本身 |
//F/ancestor-or-self::* | 所有F元素及它的祖先节点元素 | F元素、F元素的父节点C元素和A元素 |
/A/C/descendant-or-self::* | 所有A元素→C元素及它们的后代元素 | id值为c2的C元素、该元素的子元素B、D、F元素 |
/A/C/following-sibling::* | A元素→C元素的紧邻的后序所有兄弟节点元素 | 没有任何属性的E元素 |
/A/C/preceding-sibling::* | A元素→C元素的紧邻的前面所有兄弟节点元素 | id值为b1和b2的两个B元素 |
/A/B/C/following::* | A元素→B元素→C元素的后序的所有元素 | id为b2的B元素、无属性的C元素、无属性的B元素、id为d2的D元素、无属性的F元素、无属性的E元素。 |
/A/C/preceding::* | A元素→C元素的前面的所有元素 | id为b2的B元素、id为e2的E元素、id为e1的E元素、id为d1的D元素、name为b的B元素、id为c1的C元素、id为b1的B元素 |
条件匹配条件匹配就是利用一些函数的运算结果的布尔值来匹配符合条件的节点。常用于条件匹配的函数有四大类:节点函数、字符串函数、数值函数、布尔函数。例如前面提到的last()、position()等等。这些功能函数可以帮助我们精确寻找需要的节点。
函数及功能 | 作用 |
count()功能 | 统计计数,返回符合条件的节点的个数 |
number()功能 | 将属性的值中的文本转换为数值 |
substring() 功能 语法:substring(value, start, length) | 截取字符串 |
sum()功能 | 求和
|
这些功能只是XPath语法中的一部分,还有大量的功能函数没有介绍,而且目前XPath的语法仍然在不断发展中。通过这些函数我们可以实现更加复杂的查询和操作。