在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已经基本搞定了,多多指点。