昨天用wpf的时候要访问xml,弄了很久,所以记录一下。
C#3.0提供了轻型的XmlReader、XmlWriter类和完全文档对象模式(DOM)的XmlDocument类。这也是我昨天郁闷半天的原因。以前用过XmlTextReader,也没搞太懂,就是找到了方法,贴上代码。看懂下了事。
在这个博客可以找到原理:http://www.cnblogs.com/xdesigner/archive/2008/05/15/1198398.html
整个博客可以看到xml操作方法:http://www.cnblogs.com/surfsky/archive/2007/03/13/673625.html
先上原理:
XmlReader是流式处理模型,所谓流式处理是指将XML文档当做一个数据流来进行处理,逐个处理XML文档中的数据。这种模型下,读取快,内存占用少,性能也就好。它的缺点也明显(昨天我就是在这上面闷了很久):1.只能读取不能修改;2.不能使用XPath技术;3.接口简单,处理方法比较少。
System.Xml空间
以下类适合快速流式读写XML文件(注:DOM适合随机读写)
XmlReader, XmlWriter,
XmlTextReader, XmlTextWriter
XmlValidatingReader, XmlValidatingWriter 添加了DTD和模式验证,因此提供了数据的有效性验证
XmlNodeReader, XmlNodeWriter 把XmlNode作为其源
XmlDocument是DOM处理模型,使用文档对象模型的思想解析整个XML文档,在内存中生成一个对象树来表述XML文档。可以使用XmlElement对象来影射XML文档中的一个元素,使用XmlAttribute对象来影射到XML文档中的一个属性。这样通过编程可以方便的操作XML,比如递归遍历XML文档的一部分或全部,可以像树状结构插入,修改或删除XML元素,可以设置XML元素的属性。
在DOM模式下,我们可以使用XPath技术在XML文档树状结构中进行快速检索和定位,这为处理XML文档带来比较大的方便。
经常用到的几个DOM的类型;
XmlNode 是DOM结构中的所有类型的基础类型,它定义了所有XML节点的通用属性和方法,是XMLDOM的基础。它具有一个ChildNodes属性,表示它所包含的子XML节点。
XmlAttribute 表示XML属性,它只保存在XmlElement的Attributes 列表中。
XmlDocument表示XML文档本身,是XMLDOM模型中的顶级对象,它用于对XML文档进行整体的控制,并且是其它程序访问XML文档对象树的唯一入口。
XmlLinkedNode在XmlNode的基础上实现了访问前后同级节点的方法。
XmlElement元素表示XML元素。是XMLDOM中使用最多的对象类型。它具有Attributes属性可以处理它所拥有的属性,可以使用ChildNodes属性获得它所有的子节点。并提供了一些添加和删除子节点的方法。
下面看一张XDocument的(有个问题想问,XDocument和XElement的关系是?):
再上建议:
使用XmlDocument:1.传统的DOM方法适用。2.所需的整个文档皆在内存中。3.必须在内存中向前或向后遍历以及更新。
使用XmlReader/XmlWriter:1.必须适用流方法。2.必须要求进行最快的处理。3.要处理较大的文档并且无法一次性加载全部文档。
使用XElement/XDocument:1.用代码构建xml声明。2.LINQ的查询能力有助于流方法。3.需要一种针对大多数开发人员的更易读的XML编程语法。
4.要求使用查语义在内存中遍历。
博文后面的代码比较多,我只说我用到的。
XmlReader有个很经典的:
XmlReader reader = xDoc.CreateRedaer();
reader.Settings.CheckCharacters = true;
int level = 0;
while(reader.Reader())
{
switch(reader.NodeType)
{
case XmlNodeType:
Display(level,"CDATA:{0}",reader.Value);
break;
case XmlNodeType.Comment:
Display(level,"Comment:{0}",reader.Value);
break;
case XmlNodeType.DocumentType:
Display(level,"DOCTYPE:{0}= {1}",reader.Name,reader.Value);
break;
case XmlNodeType.Element:
Display(level,"Element:{0}",reader.Name);
level++;
while(reader.MoveToNextAttribute())
{
Display(level,"ATTRIBUTE: {0}='{1}'",reader.Name,reader.Value);
}
break;
case XmlNodeType.EndElement;
level--;
break;
case XmlNodeType.EntityReference:
Display(level,"ENTITY:{0}",reader.Name);
break;
case XmlNodeType.ProcessingInstruction:
Display(level,"INSTRUCTION: {0}={1}",reader.Value);
break;
case XmlNodeType.Text;
Display(level,"TEXT:{0}",reader.Value);
break;
case XmlNodeType.XmlDeclaration:
Display(level,"DECLARATION: {0}={1}",reader.Value);
break;
}
}
static void Display(int indentLevel,string format,params object[] args)
{
for(int i=0;i<indentLevel;i++)
Console.Write(" ");
Console.WriteLine(format,args)
}
XDocument的(网上有更详细的,这个是我为了解决自己的问题写的,主要是遍历某个节点下的全部子节点):
XmlDocument xmldoc = new XmlDocument();
xmldoc.Load(filePath);
XmlNodeList topM = xmldoc.DocumentElement.ChildNodes;
foreach (XmlElement element in topM)
{
if(element.Name=="Vocabulary")
{
XmlNodeList nodelist = element.ChildNodes;
Model.Vocabulary vocabulary = new Model.Vocabulary();
foreach (XmlElement el in nodelist)
{
if (el.Name == "Word")
vocabulary.Word = el.InnerText;
else if (el.Name == "Explain")
vocabulary.Explain = el.InnerText;
else if (el.Name == "IsLastSelected")
vocabulary.IsLastSelected = el.InnerText=="0"?false:true;
}
vocabularies.Add(vocabulary);
}
OK,先到这里,以后碰到问题再补上增删的。
继续:
关于linq to xml的,关于Descentdants函数。具体可以参看:http://www.programbbs.com/doc/4564.htm
Desentdants英文意思是后代,晚辈。也就是这个函数是用于获得某个Element下的指定子Element。
foreach (XElement elem in doc.Elements(\"Customers\").Descendants(\"Customer\"))
{
Console.WriteLine(string.Format(
\"Customer ID : {0}, Name : {1}, Address : {2}\",
elem.Attribute(\"ID\").Value,
elem.Attribute(\"Name\").Value,
elem.Attribute(\"Address\").Value)
);
}
<?xml version=\"1.0\" encoding=\"utf-8\"?>
<Customers>
<Customer ID=\"A0001\" Name=\"Tom\" Address=\"Taipen\" />
<Customer ID=\"A0002\" Name=\"Mary\" Address=\"LD\" />
<Customer ID=\"A0003\" Name=\"Jeff\" Address=\"YW\" />
</Customers>
特别的,当xml中包含了Namespace时,传入的Descendants的指定Element名称必包含Namespace,
Descendants函数接收的是一个XName的元素,它隐含了\算子,显然刚只得到了Element的LocalName。可以这样做:
var cstr = (from s1 in doc.Descendants() where s1.Name.LocalName==\"Connection\" select s1).First();
var cstr = (from s1 in doc.Descendants(doc.Root.Name.Namespace+\"Connection\" select s1).First();
继续:Linq to xml 的Elements()函数
(发现很多方法都可以轻松搞定我那个问题)
参考网站:http://www.cnblogs.com/ucetgg/archive/2009/04/23/1441739.html
var name = from nm in xelement.Elements("Employee") where (string)nm.Element("Sex")=="Female" select nm;
继续:处理非法字符
XElement和XmlWriter方法:
XElement可以直接处理转义也可以用XCData对象处理。
string invalidChars =@"<>\&";
XElement element = new XElement("Root",
new XElement("InvalidChars1",
new XCData(invalidChars)),
new XElement("InvalidChars2", invalidChars));
InvalidChars1将保存为<![CDATA[<>\&"]>,InvalidChars2将转义存储。
附上转义表:
< <
< >
" "
' '
& &
XmlWriter处理可以使用WriteCData方法将非法字符封装在CDATA片段中,另一种使用WriteElementString方法。
string invalidChars =@"<>\&";
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
using(XmlWriter writer = XmlWriter.Create(Console.Out,settings))
{
writer.WriteStartElement("root");
writer.WriteStartElement("InvalidChars1");
writer.WriteCDate(invalidChars);
writer.WriteEndElement();
writer.WriteElementString("InvalidChars2",invalidChars);
writer.WriteEndElement();
}
XmlDocumenthe和XmlElement处理方法:
放置于CDATA片段中,并添加至XmlElement的InnerXML属性中。
string invalidChars =@"<>\&";
XmlElement invalidElement1 = xmlDoc.CreateElement("InvalidChars1");
invalidElement1.AppendChild(xmlDoc.CreateCDataSection(invalidChars);
通过赋值给InnerText属性令XmlElement类为封装数据。
string invalidChars =@"<>\&";
XmlElement invalidElement2 = xmlDoc.CreateElement("InvalidChars2");
invalidElement2.InnerText = invalidChars;