<?xml version="1.0" encoding="utf-8" ?>
<bookstore xmlns="http://example.books.com">
<book genre="autobiography" publicationdate="1991" ISBN="1-861003-11-0">
<title>The Autobiography of Benjamin Franklin</title>
<author>
<first-name>Benjamin</first-name>
<last-name>Franklin</last-name>
</author>
<price>8.99</price>
</book>
<book genre="novel" publicationdate="1967" ISBN="0-201-63361-2">
<title>The Confidence Man</title>
<author>
<first-name>Herman</first-name>
<last-name>Melville</last-name>
</author>
<price>11.99</price>
</book>
<book genre="philosophy" publicationdate="1991" ISBN="1-861001-57-6">
<title>The Gorgias</title>
<author>
<name>Plato</name>
</author>
<price>9.99</price>
</book>
</bookstore>
后台代码页C#
using System;
using System.IO;
using System.Xml;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
int bookcount = 0;
XmlReaderSettings settings = new XmlReaderSettings();
settings.IgnoreWhitespace = true;
settings.IgnoreComments = true;
string booksFile = Server.MapPath("Books.xml");
using (XmlReader reader = XmlReader.Create(booksFile, settings))
{
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element && reader.LocalName == "book")
{
bookcount++;
}
}
}
}
}
这里使用了XmlReader.Create方法,它可以用于创建XmlReader的具体实现代码,但如果采用这种技术,就会发现它非常灵活,因为可以在创建XmlReader的其他实例时重用XmlSettings对象,因为XmlReader实现了IDisposable,所以Using关键字使用
从XmlReader的观点来看,所有的东西都是节点,包括 空白、注释、属性、元素和结束元素
提示:
Reader.LocalName属性包含节点的非名称空间限定名。Reader.Name属性与它不同,它包含节点的完全限定名(包括命名空间)
---------------------------------
使用XDocument 替代XmlReader
System.Xml.Linq命名空间引入了一个心累XDocument,类似Ling的查询语法
using System;
using System.IO;
using System.Linq;
using System.Xml.Linq;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
XDocument booksXML = XDocument.Load(Server.MapPath("Books.xml"));
var books = from book in
booksXML.Descendants("{http://sample.books.com}book")
select book.Element("{http://sample.books.com}title").Value;
}
}
无论XML文档的模式如何,第一段代码中如果文档包含book元素,代码就会给它计数。如果代码之对特定模式类型的图书进行技术,这里是指Books.xml文件中的图书,就根据Books.xsd模式进行验证
XmlReaderSettings settings = new XmlReaderSettings();
string booksSchemaFile = Server.MapPath("booksSchemaFile");
settings.Schemas.Add(null,XmlReader.Create(booksSchemaFile));
settings.ValidationType = ValidationType.Schema;
settings.ValidationFlags = XmlSchemaValidationFlags.ReportValidationWarnings;
settings.ValidationEventHandler += new ValidationEventHandler(settings_ValidationEventHandler);
包含NameTable优化
XmlReader内部使用NameTable,列出该文档中使用的所有已知的元素、属性和命名空间,这个过程称为原子化,其字面意思是XML文档被分解为各个原子部分。如果把字符串book作为一个对象引用,和其他元素名一起保存在一个表中,就不需要把book在内部结构中存储多次。
这是一个内部实现细节,但它是一种得到支持的有效方式,可以大大加速XML类的使用,泪如XmlReader 和 XmlDocument。吧name元素添加到NameTable中,使用字符串比较,来比较字符串字面值 和 reader.LoaclName。这些比较还可以转换对象引用比较,对象引用比较比字符串比较快许多,从而得到优化。另外,XML NameTable可以在System.Xml类的多个实例中共享,甚至在XmlReaders和XmlDocuments之间共享。
因为我们正在计算book元素的个数,所以创建一个包含该元素(book)的NameTable,另外我们并不比较字符串,而是比较兑现给引用
protected void Page_Load(object sender, EventArgs e)
{
int bookcount = 0;
XmlReaderSettings settings = new XmlReaderSettings();
NameTable nt = new NameTable();
object book = nt.Add("book");
settings.NameTable = nt;
string booksSchemaFile = Path.Combine(Request.PhysicalApplicationPath,"books.xsd");
settings.Schemas.Add(null, XmlReader.Create(booksSchemaFile));
settings.ValidationType = ValidationType.Schema;
settings.ValidationFlags = XmlSchemaValidationFlags.ReportValidationWarnings;
settings.ValidationEventHandler += new ValidationEventHandler(settings_ValidationEventHandler);
settings.IgnoreComments = true;
settings.IgnoreWhitespace = true;
string booksFile = Path.Combine(Request.PhysicalApplicationPath,"books.xml");
using (XmlReader reader = XmlReader.Create(booksFile))
{
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element &&
book.Equals(reader.LocalName))
{
bookcount++;
}
}
}
}
NameTable被添加到XmlSettings对象中,NameTable的Add方法返回对钢材添加的原子的对象引用,在这里,该原子存储在对象引用book中,以后book引用将用于比较reader.LoaclName属性。这里选择使用.NET Framework中所有对象都有的Equals方法,以强调这是检查对象的相等性。这两个对象要么是同一个额原子,要么不是。在NameTable上从Add方法返回的book,对象与分析XML文档 Books.xml中的book元素时读取器使用的对象相同。
在上例中,给非常少量的图书技术,但对于接近1MB的大型XML来说,性能会提升10%-15%,尤其是涉及到XmlReader的计算和处理时,性能的提升更明显。另外,由于NameTable高速缓存在XmlReaderSettings对象中,所以在XmlReaderSettings兑现给重用于其他System.Xml对象时,也会重用NameTable,这将再次提升性能。
---------------------------------
从XML中体书.NET CLR类型
protected void Page_Load(object sender, EventArgs e)
{
int bookcount = 0;
decimal booktotal = 0;
XmlReaderSettings settings = new XmlReaderSettings();
string booksSchemFile = Path.Combine(Request.PhysicalApplicationPath,"books.xsd");
NameTable nt = new NameTable();
object book=nt.Add("book");
object price = nt.Add("price");
settings.NameTable = nt;
string booksFile = Path.Combine(Request.PhysicalApplicationPath,"Books.xml");
using (XmlReader reader = XmlReader.Create(booksFile, settings))
{
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element &&
book.Equals(reader.LocalName))
{
bookcount++;
}
if (reader.NodeType == XmlNodeType.Element &&
price.Equals(reader.LocalName))
{
booktotal += reader.ReadElementContentAsDecimal();
}
}
}
}
上例中的booktotal变量被强类型化为一个小数值
----------------------------------
ReadSubtree 和XmlSerialization
XmlReader不仅可以从XML中提取简单类型,还可以是哟娜韩国XML串行化和ReadSubtree提取更复杂的类型
XML串行化器可以给已有的类添加属性,为XML串行化器提供如何把对象标识为XML的提示。XML串行化器只能串行化对象的公共属性,不能串行化私有属性。
在创建XmlSerializer时,要把一个Type对象传入构造函数,XmlSerializer就会使用反射检查该对象是否能创建一个临时的程序集,该程序集知道如何把对象读写为XML。XmlSerializer使用XmlReader内部的一个具体实现方式来串行化这些对象。
下列中使用ReadSubtree和一个新的类型化Author类,该类已是哟还能够XML串行化属性进行了标记,ReadSubtree会在当前位置分解一个新的XmlReader,该XmlReader传送给XmlSerializer,并创建一个复杂类型。Author类包含XmlElement属性,它表示尽管有一个FirstName属性,但它应该串行化和反串行化为“姓”
using System.Xml.Serialization;
[XmlRoot(ElementName="author",Namespace="http://example.books.com")]
public class Author
{
[XmlElement(ElementName = "first-name")]
public string FirstName;
[XmlElement(ElementName = "last-name")]
public string LastName;
}
XmlSerializerFactory factory = new XmlSerializerFactory();
using (XmlReader reader = XmlReader.Create(booksFile, settings))
{
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element &&
book.Equals(reader.LocalName))
{
XmlSerializer xs = factory.CreateSerializer(typeof(Author));
Author a = (Author)xs.Deserialize(reader.ReadSubtree());
}
}
}
-----------------------------
通过LINQ to XML 从 XML中创建CLR对象
在XmlSerialize和System.Xml.Linq之间没有直接的关联,但有一种非常简单的方法可以在LINQ to XML语法中创建CLR对象。这个语法比传统的XmlSerializer更灵活、宽泛,如下程序所示
XDocument booksXML = XDocument.Load(Server.MapPath("Books.xml"));
XNamespace ns = "http://sample.books.com";
var authros = from book in booksXML.Descendants(ns + "author")
select new Author
{
FirstName = book.Element(ns + "first-name").Value,
LastName = book.Element(ns + "last-name").Value
};
--------------------------------------------
用XmlWriter创建XML
XmlWriter的工作方式类似XmlReader,但顺序相反,使用字符床连接来快速创建XML文档,或XML片段时非常吸引人的,但XML是InfoSet的表示,不是尖括号
XmlWriter还有一个设置类XmlWriterSettings。这个类包含缩进、换行、编码、XML一致级别等选项。在下列中创建了一个XML文档bookstore,并把它直接输出到页面上。ASPX页面上的所有HTML标记都必须删除,这样才能正确输出XML文档。输出XML的另一种简单方式是使用ASHX HttpHandler
protected void Page_Load(object sender, EventArgs e)
{
Double price = 49.99;
DateTime publicationdate = new DateTime(2005,1,1);
String isbn = "1-057-610-0";
Author a = new Author();
a.FirstName = "Scott";
a.LastName = "Hanselman";
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.NewLineOnAttributes = true;
Response.ContentType = "text/xml";
XmlSerializerFactory factory = new XmlSerializerFactory();
using (XmlWriter writer = XmlWriter.Create(Response.OutputStream, settings))
{
writer.WriteStartDocument();
writer.WriteStartElement("bookstore");
writer.WriteStartElement("book");
writer.WriteStartAttribute("publicationdate");
writer.WriteValue(publicationdate);
writer.WriteEndAttribute();
writer.WriteStartAttribute("ISBN");
writer.WriteValue(isbn);
writer.WriteEndAttribute();
writer.WriteElementString("title","ASP.NET");
writer.WriteStartAttribute("price");
writer.WriteValue(price);
writer.WriteEndAttribute();
XmlSerializer xs = factory.CreateSerializer(typeof(Author));
xs.Serialize(writer, a);
writer.WriteEndElement();
writer.WriteEndElement();
writer.WriteEndDocument();
}
}
----------------------------------------
使用LINQfor XML 创建XML
使用LINQ for XML不如XmlWrite快,但仍非常快,切非常容易阅读。
Double price = 49.99;
DateTime publicationdate = new DateTime(2005,1,1);
String isbn = "1-057-610-0";
Author a = new Author();
a.FirstName = "Scott";
a.LastName = "Hanselman";
Response.ContentType = "text/xml";
XNamespace ns = "http://sample.books.com";
XDocument books = new XDocument(
new XElement(ns+"bookstore",
new XElement(ns+"book"),
new XAttribute("publicationdate",publicationdate),
new XAttribute("ISBN",isbn),
new XElement(ns +"title","ASP.net"),
new XElement(ns+"price",price),
new XElement(ns+"author",
new XElement(ns+"first-name",a.FirstName),
new XElement(ns+"last-name",a.LastName)
)
)
);
----------------
使用XPathDocumnet,下例找出价格低于10美元的图书
XPahtDocument 把一个 XPathNavigator作为调用CreateNavigator的结果。XPathNavigator使用一个传送给Select方法的XPath来查询,并返回一个XPathNodeIterator。该XPathNodeIterator可以通过IEnumberable来遍历。例子使用了一个制度XPathDocument,并没有更新内存中的数据
tring booksFile = Server.MapPath("Books.xml");
XPathDocument document = new XPathDocument(booksFile);
XPathNavigator nav = document.CreateNavigator();
XmlNamespaceManager namespaceMgr = new XmlNamespaceManager(nav.NameTable);
namespaceMgr.AddNamespace("b", "http://sample.books.com");
foreach(XPathNavigator node in
nav.Select("//b:book[not(b:price[. > 10.00])]/b:price)",namespaceMgr))
{
Decimal price = (decimal)node.ValueAs(typeof(decimal));
}
如果要以XPathNavigator的形式修改底层的XML节点,就应该使用XmlDocument代替XPathDocument.XPath表达式的计算可能比较慢,但应该可以编辑。注意它在性能上的妥协。在大多数情况下,应尽可能使用只读的XPathDocument;如下修改
string booksFile = Server.MapPath("Books.xml");
XmlDocument document = new XmlDocument();
document.Load(booksFile);
XPathNavigator nav = document.CreateNavigator();
XmlNamespaceManager namespaceMgr = new XmlNamespaceManager(nav.NameTable);
namespaceMgr.AddNamespace("b", "http://sample.books.com");
foreach(XPathNavigator node in
nav.Select("//b:book[not(b:price[. > 10.00])]/b:price)",namespaceMgr))
{
Decimal price = (decimal)node.ValueAs(typeof(decimal));
node.SetTypedValue(price * 1.2M);
}
-------------------------
将DataSet 保存到XML中
string cnnStr = "database=Northwind;Data Source=localhost;User id=sa;pwd=sa";
using (SqlConnection conn = new SqlConnection(cnnStr))
{
SqlCommand command = new SqlCommand("select * from customers",conn);
conn.Open();
DataSet ds = new DataSet();
ds.DataSetName = "Customers";
ds.Load(command.ExecuteReader(), LoadOption.OverwriteChanges, "Customers");
Response.ContentType = "text/xml";
ds.WriteXml(Response.OutputStream);
}
XmlDataDocument
DataSet使用XML时的格式不太灵活,但XmlDocument类不是这样。为了在它们之间架起桥梁,引入了一个不太常见的混合对象XmlDataDocument. 这个对象保留了所有的XML结构,可以通过XmlDocument API访问XML,切不会丧失关系API的灵活性。XmlDataDocument包含它自己的DataSet,可以称为支持DataSet。它的内部DataSet提供了XML数据的关系视图。包含在XML数据文档中,切没有映射到关系视图上的所有数据都不会丢失,而且这写数据可以用于DataSet的API。
XmlDataDocument是一个构造函数,它把DataSet作为一个参数。对XmlDataDocument的任何修改都会反映到DataSet上,反之亦然。
string cnnStr = "database=Northwind;Data Source=localhost;User id=sa;pwd=sa";
using (SqlConnection conn = new SqlConnection(cnnStr))
{
SqlCommand command = new SqlCommand("select * from customers",conn);
conn.Open();
DataSet ds = new DataSet();
ds.DataSetName = "Customers";
ds.Load(command.ExecuteReader(), LoadOption.OverwriteChanges, "Customers");
XmlDataDocument doc = new XmlDataDocument(ds);
doc.DataSet.EnforceConstraints = false;
XmlNode node = doc.SelectSingleNode(@"//Customer[CustomerID='ANATR']/ContatcTitle");
node.InnerText="Boss";
doc.DataSet.EnforceConstraints = true;
Response.ContentType = "text/xml";
ds.WriteXml(Response.OutputStream);
}
DataSet 的属性 EnforceConstraints被设置为false,以允许修改DataSet。
-----------------------------------------
数据库和XML
XML可以来自于任何数据源,SQL Server 和ADO都支持XML,例如System.Data.SqlCommand类的ExectuteXmlReader方法。在SQL Server2000上对XML的支持包括SQLXML3.0 机器XML扩展,SQL Server2005 内置了对XML数据类型的支持,
可以使用FOR XML AUTO 子句修改SQL 查询,使之返回XML。如果有一个简单的查询,如 select * from customers 就可以修改语句,如下所示
select * from customers as customer FOR XML AUTO
XML AUTO 返回XML片段,而不是带有文档元素的完整XML文档。数据库中的每一行都将成为一个元素,数据库中的每一列都将成为元素上的一个属性。
<customers CustomerID="ALFKI" CompanyName="Alfreds Futterkiste" ContactName="Maria Anders" ContactTitle="Sales Representative" Address="Obere Str. 57" City="Berlin" PostalCode="12209" Country="Germany" Phone="030-0074321" Fax="030-0076545"/><customers CustomerID="ANATR" CompanyName="Ana Trujillo Emparedados y helados" ContactName="Ana Trujillo" ContactTitle="Owner" Address="Avda. de la Constitución 2222" City="México D.F." PostalCode="05021" Country="Mexico" Phone="(5) 555-4729" Fax="(5) 555-3745"/>
如果 select * from customers FOR XML AUTO,ELEMENTS
<customers><CustomerID>ALFKI</CustomerID><CompanyName>Alfreds Futterkiste</CompanyName><ContactName>Maria Anders</ContactName><ContactTitle>Sales Representative</ContactTitle><Address>Obere Str. 57</Address><City>Berlin</City><PostalCode>12209</PostalCode><Country>Germany</Country><Phone>030-0074321</Phone><Fax>030-0076545</Fax></customers><customers><CustomerID>ANATR</CustomerID><CompanyName>Ana Trujillo Emparedados y helados</CompanyName><ContactName>Ana Trujillo</ContactName><ContactTitle>Owner</ContactTitle><Address>Avda. de la Constitución 2222</Address><City>México D.F.</City><PostalCode>05021</PostalCode><Country>Mexico</Country><Phone>(5) 555-4729</Phone><Fax>(5) 555-3745</Fax></customers>
下面的示例是一个返回定制XML的查询,查询开头的WITH NAMESPACES定义了一个默认的命名空间,并使用列样式别名将命名空间与命名空间前缀关联起来。在这个示例中,addr:shi urn:hanselman.com/northwind/address 的前缀WITH XMLNAMESPACES( 'urn:hanselman.com/norhtwind/address' as addr,
DEFAULT 'nrn:hanselman.com/northwind')
SELECT CustomerID as 'ID',
CompanyName,
Address as 'addr:Address/addr:Street',
City as 'addr:Address/addr:City',
Region as 'addr/Address/addr:Region',
PostalCode as 'addr:Address/addr:Zip',
Country as 'addr:Address/addr:Country',
ContactName as 'Contact/Name',
Phone as 'Contact/Phone',
Fax as 'Contact/Fax'
FROM Customers
FOR XML PATH('Customer'),ROOT('Customers'),ELEMENTS XSINIL
用AS官方尖子生名的别名描述了元素及其嵌套关系,而PATH关键字定义了Customers表示的元素。ROOT关键字定义了文档的根元素。
ELE<ENTS关键字和XSINIL描述了处理null的方式,没有这些关键字,就不能为行中包含null的列创建XML元素。如果数据库中没有这些数据,就会使得到的XML文档缺失数据。有了ELEMENTS和XSINIL元素就能显示的 xsi:nil 语法输出。