原文http://www.cnblogs.com/ListenFly/archive/2011/10/17/2206719.html
在Silverlight中操作Xml有三种方式,分别是使用XmlReader(XmlWriter),Linq to xml ,XmlSerializer(进行Xml的序列化)。
下面列出微软.net类库提供的读写xml文件个类及其特点:
类名称 | 优点 | 缺点 |
XmlReader | 快速、高效、可扩展 | 只读,只向前,需要人工验证 |
XmlDocument | 可往返、可读写、支持XPath筛选 | 比XmlReader慢 |
XPathNavigator | 可往返,支持XPath和XSLT | 只读 |
XPathDocument | 比XmlDocument,优化支持XPath和XSLT | 比XmlReader慢 |
一、使用XmlReader读取Xml
以下为Xml内容
<?xml version="1.0" encoding="utf-8" ?><Menus><Menu title="常用网址"><item name="天下网" url="http://www.netskycn.com" id="1"/><item name="天下网生活论坛" url="http://life.netskycn.com" id="2"/><item name="csdn" url="http://www.csdn.net" id="3"/><item name="我的博客" url="http://blog.csdn.net/zhoufoxcn" id="4"/><item name="百度" url="http://www.baidu.com" id="5"/><item name="Google" url="http://www.google.cn" id="6"/><item name="微软" url="http://www.microsoft.com" id="7"/></Menu><Menu title="娱乐网址"><item name="奇虎" url="http://www.qihoo.com" id="12"/><item name="网易" url="http://www.163.com" id="13"/><item name="天涯" url="http://www.tianya.cn" id="14"/></Menu><Menu title="安全网址"><item name="360" url="http://www.safe360.com" id="15"/><item name="瑞星" url="http://www.rising.com.cn" id="16"/></Menu></Menus>
读取这个Xml,这里的stream参数是通过WebClient下载Xml返回的流对象,在上边的对比中已经说了XmlReader需要手动验证,这里就使用了switch对XmlReader的
NodeType(节点)进行判断.
使用XmlReader.Create创建一个XmlReader对象;
使用Read方法循环读取到下一个节点;
XmlNodeType是枚举类型,表示节点的类型;
XmlReader.AttributeCount,表示当前节点的属性的数量,xmlReader.MoveToAttribute(i)移动到指定的Attribute,参数可以是属性的索引,也可以是属性的Name;
XmlReader.Name表示当前节点的名称,XmlReader.Value表示当前节点的文本值.
private void SetText(Stream stream) { StringBuilder sbXml = new StringBuilder(); XmlReader xmlReader = XmlReader.Create(stream);while (xmlReader.Read()) { sbXml.Append("节点类型:" + xmlReader.NodeType + "==");switch (xmlReader.NodeType) {case XmlNodeType.XmlDeclaration:for (int i = 0; i < xmlReader.AttributeCount; i++) { xmlReader.MoveToAttribute(i); sbXml.Append("属性:" + xmlReader.Name + "=" + xmlReader.Value + " "); }break;case XmlNodeType.Attribute:for (int i = 0; i < xmlReader.AttributeCount; i++) { xmlReader.MoveToAttribute(i); sbXml.Append("属性:" + xmlReader.Name + "=" + xmlReader.Value + " "); }break;case XmlNodeType.CDATA: sbXml.Append("CDATA:" + xmlReader.Value + " "); break;case XmlNodeType.Element: sbXml.Append("节点名称:" + xmlReader.LocalName+"\n"); for (int i = 0; i < xmlReader.AttributeCount; i++) { xmlReader.MoveToAttribute(i); sbXml.Append("属性:" + xmlReader.Name + "=" + xmlReader.Value + " "); }break;case XmlNodeType.Comment: sbXml.Append("Comment:" + xmlReader.Value);break;case XmlNodeType.Whitespace: sbXml.Append("Whitespace:" + " ");break;case XmlNodeType.ProcessingInstruction: sbXml.Append("ProcessingInstruction:" + xmlReader.Value);break;case XmlNodeType.Text: sbXml.Append("Text:" + xmlReader.Value);break; } } xmlReader.Close(); txtXml.Text = sbXml.ToString(); }
下边看一个读取在项目中可能有此结构的xml.
xml内容如下:
<?xml version="1.0" encoding="utf-8" ?> <Persons><Person Name="FLY" Age="21"> My name is wangyafei.</Person></Persons>
读取代码如下,在读取代码中循环读取了Person节点中的属性(Name和Age).
private void ReadXml(Stream stream) { XmlReader xmlReader = XmlReader.Create(stream); StringBuilder sbXml = new StringBuilder();while (xmlReader.Read()) { sbXml.Append("\n"); sbXml.Append("节点类型:" + xmlReader.NodeType.ToString());switch (xmlReader.NodeType) {case XmlNodeType.DocumentType:break;case XmlNodeType.Element://得到节点的名称 string elementName = xmlReader.Name; sbXml.Append("\n"); sbXml.Append("标签名称:" + elementName);//循环节点的属性 for (int i = 0; i < xmlReader.AttributeCount; i++) { xmlReader.MoveToAttribute(i); sbXml.AppendFormat(string.Format(" 属性名称{0}:属性值{1}", xmlReader.Name, xmlReader.Value)); }break;case XmlNodeType.EndElement: elementName = xmlReader.Name; sbXml.Append(" "+elementName + " element end");break;default:break; } } txtXml.Text = sbXml.ToString(); }
下面通过一张图(非原创)看看XmlReader是怎么读取一个Xml的,可以清晰 的看到读取的顺序,首先读取xml的声明,然后就是每个标签后边的空白(可能该标签存在文本,那就是读取文本),接着读取接下来的节点部分,接下来(如果 节点存在属性则循环读取属性,如果存在嵌套节点则读取嵌套节点的内容,同时可能睡重复本步骤,如果存在文本则在读取完节点的属性后读取文本,然后读取节点 的结束标签,一直读取直到整个Xml的结尾标签完毕):
说完了XmlReader,看看XmlWriter怎么来进行写入Xml。
以下代码创建一个简单的xml,XmlWriterSettings主要是用于创建Xml时候的一些设置,在这里设置了Indent和IndentChars(这些属性可以通过MSDN获取资料).
使用XmlWriter.Create方法创建一个XmlWriter对象。
WriteComment写入一个注释;
WriteStartElement("ElementName"),创建一个xml节点,和WriteEndElement对应;
WriteAttributeString("attributeName","attributeValue")给Xml节点添加属性,另外还可以给节点引用一个命名空间
WriteAttributeString("xmlns","前缀","命名空间的URL","attributeValue"),还可以使用指定的前缀创建节点,
WriteAttributeString("ElementName","value(命名空间的值)","文本值");
WriteString("Value"),向节点中写入文本;
WriteElementString("ElementName","Value"),创建一个包含文本的节点;
LookupPrefix("value")得到指定值对应的前缀,可以参见WriteAttributeString如何添加一个命名空间;
Flush提交流;
Close关闭XmlWriter.
private void WriteXml(StringBuilder sbXml) { XmlWriterSettings settings = new XmlWriterSettings();//设置Xml是否缩进 settings.Indent = true;//缩进使用的是空格 settings.IndentChars=(" ");//使用Create方法创建一个XmlWriter对象,传入Stream对象和XmlWriterSettings using (XmlWriter writer=XmlWriter.Create(sbXml)) { writer.WriteComment("sample XML fragment");// 创建一个根节点book writer.WriteStartElement("book");// 定义一个namespace writer.WriteAttributeString("xmlns", "bk", null, "urn:samples");//添加一个genre属性,值为novel writer.WriteAttributeString("genre", "novel");// 添加一个子节点title writer.WriteStartElement("title");//给节点添加文本 writer.WriteString("The Handmaid's Tale");//title节点结尾 writer.WriteEndElement();// 也是创建一个节点,仅仅包括文本(19.95) writer.WriteElementString("price", "19.95");//使用命名空间的前缀创建一个ISBN节点 string prefix = writer.LookupPrefix("urn:samples"); writer.WriteStartElement(prefix, "ISBN", "urn:samples");//写入文本 writer.WriteString("1-861003-78"); writer.WriteEndElement();// Write the style element (shows a different way to handle prefixes). writer.WriteElementString("style", "urn:samples", "hardcover");// Write the close tag for the root element. writer.WriteEndElement();// 提交xmlwriter writer.Flush(); writer.Close(); } txtXml.Text = sbXml.ToString(); HtmlPage.Window.Alert(sbXml.ToString()); }
创建后的内容是:
<?xml version="1.0" encoding="utf-16"?><!--sample XML fragment--><book xmlns:bk="urn:samples" genre="novel"><title>The Handmaid's Tale</title><price>19.95</price><bk:ISBN>1-861003-78</bk:ISBN><bk:style>hardcover</bk:style></book>
二、使用Linq to xml操作Xml文件
首先定义一个XDocument(表示一个XML文件对象,当然也可以使用XElement对象),然后创建一个XElement表示节点(可以嵌套多层节点和添加属性XAttribute对象),指定Declaration属性表示Xml的声明部分.
创建Xml对象:
//创建Xml对象 XDocument document = new XDocument();//创建注释 XComment commen = new XComment("创建Xml");//定义一个xml处理指令 XProcessingInstruction instruction = new XProcessingInstruction("xml-stylesheet", "href='mystyle.css' title='Compact' type='text/css'");//创建一个Xml几点,并且添加属性Name和Age,添加子节点Company XElement PersonElement = new XElement("Person", new XAttribute("Name", "Fly"),new XAttribute("Age", "21"),new XElement("Company", new XAttribute("Name", "XXXXXX")));//定义xml的声明 document.Declaration = new XDeclaration("1.0", "utf-8", "true");//将注释,处理指令以及节点添加到Document中去。 document.Add(commen); document.Add(instruction); document.Add(PersonElement);
读取Xml:
通过WebClient下载Xml文件,得到Stream,然后使用XDocument.Load(stream)加载xml,使用XDocument.ToString得到整个xml的内容,当然也可以使用
XDocument.Element或者XDocument.Elements来得到要获得的节点内容,甚至可以使用Linq to xml进行加载xml。
void myWebClient_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e) {if (e.Error != null) { txtXml.Text = e.Error.Message; }else {using (Stream stream = e.Result) { XDocument document = XDocument.Load(stream);//得到Person节点 XElement element = document.Element("Person");//获得Person节点的Name属性和Age属性的值 string name=element.Attribute("Name").Value;string age = element.Attribute("Age").Value;//SaveOptions.OmitDuplicateNamespaces在进行序列化时移除重复的命名空间声明 txtXml.Text = document.ToString(SaveOptions.OmitDuplicateNamespaces); } } }
Linq to xml有几个常用的方法,使得Linq to xml非常的方便:
Save、CreateReader、ToString和WriteTo方法是比较常用的三个方法:
操作Xml(插入,修改):
使用这两个方法可以给Element添加节点
XDocument document = XDocument.Parse("<Persons> <Person > My name is wangyafei.</Person></Persons>"); XElement personElement= document.Element("Person"); personElement.AddAfterSelf("Brithday",DateTime.Now.ToShortDateString());
得到的结果是
<Persons><Person> My name is wangyafei.</Person><Brithday>2011-10-17</Brithday></Persons>
更新Xml:
删除Xml使用Remove(XElement)与RemoveAll方法来删除xml。
三、使用XmlSerializer进行序列化
XmlSerializer顾名思义Xml序列化,ok,首先创建一个实体类,可以看到给属性加上了[XmlElement]特性,表示一个Xml中对应的节点
public class Person {private string name; [XmlElement]public string Name {get { return name; }set { name = value; } }private string email; [XmlElement]public string Email {get { return email; }set { email = value; } } }
xml如下:
<?xml version="1.0" encoding="utf-8" ?> <Person><Name firstName="Super" lastName="Fly">MyFly</Name><Email>wangyafei_it@163.com</Email></Person>
下面进行序列化:
创建一个webservice,添加方法,其实这个方法才是重点,实例化一个XmlSerializer参数就是我们的Xml对应的序列化的实体类,然后调用Deserialize进行反序列化
然后转换为真正的类型,即可:
[OperationContract]public Person ResolveXmlSerializer(Stream stream) { XmlSerializer serializer = new XmlSerializer(typeof(Person)); Person person = serializer.Deserialize(stream) as Person;return person; }
通过WebClient下载Xml然后调用此方法:
private void btnDownLoad_Click(object sender, RoutedEventArgs e) { Uri endPoint = new Uri("http://localhost:4276/Images/PersonTest.xml"); WebClient client = new WebClient(); client.OpenReadCompleted += new OpenReadCompletedEventHandler(client_OpenReadCompleted); client.OpenReadAsync(endPoint); } void client_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e) { XmlServiceClient serviceClient = new XmlServiceClient(); serviceClient.ResolveXmlSerializerCompleted += new EventHandler<XmlService.ResolveXmlSerializerCompletedEventArgs>(serviceClient_Resolv eXmlSerializerCompleted);byte[] bytes=new byte[e.Result.Length]; e.Result.Read(bytes,0,bytes.Length); serviceClient.ResolveXmlSerializerAsync(bytes); } void serviceClient_ResolveXmlSerializerCompleted(object sender, XmlService.ResolveXmlSerializerCompletedEventArgs e) { Person person = e.Result; HtmlPage.Window.Alert("Name:" + person.Name + "; Email:" + person.Email); }
好了,整个Silverlight中访问xml已经基本搞定了,多多指点。