第一章 ASP.Net内建对象
第一章 ASP.Net内建对象
ASP.Net为保持用户的数据和信息,内建了许多对象,包括Application、Response、Request、cookie、Sessions、Cache、Server和ViewState等对象。通过这些对象,可以提供网站一些必不可少的功能,例如当前目录的获得、在线人数、访问网站总人数、网上商店购物筐等。本章介绍这些内建对象的属性和用法。
11.1 Response对象
使用Response对象可以向浏览器发送信息,包括直接发送信息在浏览器中显示、重定向浏览器去显示另一个网页、设置cookie的值。在ASP.Net中一般不用Response对象发送信息到浏览器中显示,可以用其它方法重定向浏览器去显示另一个网页,因此在ASP.Net中使用Response对象的机会越来越少了,本节只对Response对象做简单介绍,设置cookie方法在另一节介绍。
11.1.1用Response发送html标记在浏览器中显示
例子e11_1_1:用Response发送html标记在浏览器中显示的网页文件如下:
<%@ Page language="c#" %>
<html>
<body>
<% Response.Write("<font Size=7>");
Response.Write("Response对象使用");
Response.Write("</font");
Response.Write("<br>");
%>
</body>
</html>
11.1.2用Response对象重定向浏览器
例子e11_1_2:用Response对象重定向浏览器显示新浪网主页的例子如下:
<html>
<script language="c#" runat=server>
void EnterBtn_Click(Object src,EventArgs e)
{ Response.Redirect("http://www.sina.com.cn");
}
</script>
<body>
<form runat=server>
<asp:Label runat=server>单击按钮打开新浪网主页</asp:Label><br>
<asp:button text="打开新浪网" Onclick="EnterBtn_Click" runat=server/>
</form>
</body>
</html>
这里实现的功能完全可以用HyperLink控件实现,请读者试一试。但是如果根据条件用语句实现转向不同网页,使用此语句还是必要的,例如,有些用户企图不经过登录直接访问网站网页,在网站网页的Page_Load方法中要进行判断,如果未登录,可用上述方法直接转向登录界面。例子e11_2_1C中使用Response对象重定向时,向另一个网页传递数据。
11.2 Request对象
Request对象主要有以下用途:第一用来在不同网页之间传递数据,第二是Web服务器可以使用Request对象获取用户所使用的浏览器的信息,第三是Web服务器可以使用Request对象显示Web服务器的一些信息,最后,可以用Request对象获得Cookie信息。本节主要介绍前三种用途,有一节专门介绍Cookie。
11.2.1 用Request对象获取另一个网页传递的数据
从一个网页链接到另一个网页时,可能需要传递一些数据到另一个网页。一般采用如下格式:URL?数据名称=数据值&数据名称=数据值…,其中?表示URL后边要传递数据,数据传递的格式为:数据名称=数据值,两个数据之间用&分割。当数据传递到另一个网页时,另一个网页用Request["数据名称"]的方法取出这个数据。e10_6_7也是两个网页之间传递数据的例子。
例子e11_2_1A:本例用HyperLink打开e11_2_1B网页,传递固定数据,网页文件如下:
<html>
<body>
<form method=POST runat=server>
<asp:HyperLink id="hyperlink1" Target="_new"
NavigateUrl="e11_2_1B.aspx?Num=12345&Name=张三"
Text="提交" runat="server"/>
</form>
</body>
</html>
例子e11_2_1B:本例网页显示另一网页传递来的数据,网页文件如下:
<html>
<script language="c#" runat=server>
void Page_Load(Object src,EventArgs e)
{ Label1.Text="另一网页传递的数据是:"+Request["Num"]+Request["Name"];
}
</script>
<body>
<form runat=server>
<asp:Label id="Label1" runat=server />
</form>
</body>
</html>
例子e11_2_1C:如传递的数据是变量,例如TexBox控件输入的内容,可用如下办法:
<html>
<script language="c#" runat=server>
void button1_Click(Object src,EventArgs e)
{ string s="Num="+HttpUtility.UrlEncode(Label1.Text);
s+="&Name="+HttpUtility.UrlEncode(textBox1.Text);
Response.Redirect("e11_2_1B.aspx?"+s);
}
</script>
<body>
<form runat=server>
<asp:Label id="Label1" Text="12345" runat=server />
<asp:TextBox id="textBox1" Text="" runat=server />
<asp:button text="提交" Onclick="button1_Click" runat=server/>
</form>
</body>
</html>
在URL中,有些ASCII字符具有特殊含义,必须做特殊处理,例如字符/,用HttpUtility.UrlEncode方法将对具有特殊含义字符做特殊处理。以后将看到,通过Application和Session对象也可以在两个网页之间传递数据。
11.2.2 用Request对象获取客户端浏览器的信息
不同浏览器或相同浏览器的不同版本支持不同的功能,Web应用程序可能要根据不同的浏览器采取不同的措施,可用HttpRequest.Browser属性的HttpBrowserCapabilities对象获得用户使用的浏览器信息。见下例:
<html>
<script language="c#" runat=server>
void Page_Load(Object src,EventArgs e)
{ HttpBrowserCapabilities bc = Request.Browser;
string S="浏览器的特性如下:"+"<br>";
S+="Type="+bc.Type+"<br>";
S+="Name="+bc.Browser+"<br>";
S+="Version="+bc.Version+"<br>";
S+="Major Version="+bc.MajorVersion+"<br>";
S+="Minor Version="+bc.MinorVersion+"<br>";
S+="Platform="+bc.Platform+"<br>";
S+="Is Beta="+bc.Beta+"<br>";
S+="Is Crawler="+bc.Crawler +"<br>";
S+="Is AOL="+bc.AOL +"<br>";
S+="Is Win16="+bc.Win16+"<br>";
S+="Is Win32="+bc.Win32+"<br>";
S+="Supports Frames="+bc.Frames+"<br>";
S+="Supports Tables="+bc.Tables+"<br>";
S+="Supports Cookies="+bc.Cookies+"<br>";
S+="Supports VB Script="+bc.VBScript+"<br>";
S+="Supports Java Script="+bc.JavaScript+"<br>";
S+="Supports Java Applets="+bc.JavaApplets +"<br>";
S+="Supports ActiveX Controls="+bc.ActiveXControls+"<br>";
S+="CDF="+bc.CDF+"<br>";
Label1.Text=S;
}
</script>
<body>
<form runat=server>
<asp:Label id="Label1" runat=server></asp:Label>
</form>
</body>
</html>
11.2.3 用Request对象获取服务器信息
例子e11_2_3:用Request对象获取Web服务器信息的例子如下:
<html>
<script language="c#" runat=server>
void Page_Load(Object src,EventArgs e)
{ string s="Web服务器的特性如下:"+"<br>";
foreach(string Name in Request.ServerVariables)
{ s+=Name+":"+Request.ServerVariables(Name)+"<br>"; }
Label1.Text=s;
}
</script>
<body>
<form runat=server>
<asp:Label id="Label1" runat=server></asp:Label>
</form>
</body>
</html>
11.3 Cookie对象
用户用浏览器访问一个网站,Web服务器并不能知道是哪一个用户正在访问。但一些网站,希望能够知道访问者的一些信息,例如是不是第一次访问,访问者上次访问时是否有未做完的工作,这次是否为其继续工作提供方便等。用浏览器访问一个网站,可以在此网站的网页之间跳转,当从第一个网页转到第二个网页时,第一个网页中建立的所有变量和对象都将不存在。有时希望为这些被访问的网页中的数据建立某种联系,例如一个网上商店,访问者可能从网站中不同的网页选取各类商品,那么用什么办法记录该访问者选取的商品,也就是一般所说的购物筐如何实现。用Cookie对象可以解决以上问题。
11.3.1Cookie对象的用法
Cookie为Web应用程序保存用户相关信息提供了一种有用的方法。支持Cookie对象的浏览器允许Web应用程序将一小段文本信息存储到浏览器所在的计算机中。当用户访问网站时,web应用程序可以利用Cookie保存用户的一些信息,这样,当用户下次访问网站时,Web应用程序就可以检索到以前保存的信息。
Cookie对象采用键/值对的方法记录数据,用法如下:
HttpCookie MyCookie=new HttpCookie("UserInfo");//UserInfo为键
myCookie.Value=2.ToString();//UserInfo键的值为2
myCookie.Expires=DateTime.Now.AddHours(1);//数据1小时后无效,否则退出网页失效
Response.Cookies.Add(MyCookie);//将MyCookie写到浏览器
语句myCookie.Expires=DateTime.Now.AddHours(1)指定Cookie中数据何时失效,这里是1小时后失效。如果不指定失效时间,退出网页立即失效。取出Cookie的值的方法如下:
HttpCookie myCookie=Request.Cookies["UserInfo"];//得到键为UserInfo的Cookie
Int Num=Convert.ToInt16(myCookie.Value);
一个键还可以有若干子键,可以在一个 Cookie 中保存多个键/值对,具体方法如下:
HttpCookie MyCookie=new HttpCookie("UserInfo");//主键="UserInfo"
MyCookie.Values.Add("UserName","张三");//子键1="UserName",其值为"张三"
MyCookie.Values.Add("UserAge","13");//子键2="UserAge",其值为"13"
Response.Cookies.Add(MyCookie);//写到用户计算机中
取出Cookie的值的方法如下:
HttpCookie myCookie=Request.Cookies["UserInfo"];//得到主键为"UserInfo"的Cookie
string s=myCookie.Value("UserName");//得到子键1的值
string s=myCookie.Value("UserAge");//得到子键2的值
11.3.2用Cookie对象记录访问的次数
例子e11_3_1:本例用Cookie对象记录访问者是第几次访问本站,并将次数显示出来。
<%@ Page Language="C#" Debug="true" %>
<html>
<script language="c#" runat=server>
void Page_Load(Object src,EventArgs e)
{ if(!Page.IsPostBack)//如果网页响应事件后刷新,访问次数不加1
{ int Num=1;
HttpCookie myCookie=Request.Cookies["VistNum"];//取出Cookies
DateTime now = DateTime.Now;//得到当前时间
if(myCookie!=null)//名称为VistNum的Cookies是否存在
{ Num=Convert.ToInt16(myCookie.Value);
Num++;//如果存在,取出Cookies存的值,加1
}
else
{ myCookie=new HttpCookie("VistNum");//如果不存在,创建Cookies
}
myCookie.Value=Num.ToString();
myCookie.Expires=now.AddHours(1);//数据1小时后无效,否则退出网页失效
Response.Cookies.Add(myCookie);// Cookies中的值不能修改,只能覆盖
label1.Text="您是第"+Num.ToString()+"次访问本站";
}
}
</script>
<body>
<form runat=server>
<asp:Label id="label1" runat=server></asp:Label>
</form>
</body>
</html>
当然,浏览器的Cookies必须设置为允许使用。
11.3.3网上商店购物筐实现
网上商店一般有多个网页,每个网页提供不同种类的商品,供用户选择。用户可以浏览这些网页,从每个网页中选择商品,网上商店网站要记录用户要购买的这些商品,一般把这个功能叫做购物筐,下边的例子介绍用Cookies实现购物筐的方法。
首先用ACCESS数据库系统创建一个仓库管理系统数据库DepotI,仅有1个表goods,记录仓库中的所有商品。一般商品表要包括很多字段:编号、货物名称、包装类型、单价、数量等。这里只是说明问题,为了简单,只包括3个字段:货物编号字段gID,自动编号,主关键字;货物名称字段gName,文本,字段大小26,必填字段,默认值为空;货物数量字段gNum,整型,必填字段,默认值为0。增加4个记录,字段gID、gName、gNum的值分别为:1、香蕉、20;2、苹果、50;3、菊花、30;4、茉莉、20。
例子中有两个网页,一个网页显示水果,这里只有两种:香蕉和苹果;一个网页显示花卉,这里也只有两种:菊花和茉莉。分别在2个网页中用网格控件DataGrid显示,每个网格控件有4列,数据库DepotI的Goods表的3个字段列,还包括1列按钮列,单击按钮把所选货物放到购物筐,单击1次,数量增加1个。
例子e11_3_2A:第一个网页显示水果,网页文件如下:(运行效果如上图)
<%@ Import Namespace="System.Data.OleDb" %>
<%@ Import Namespace="System.Data" %>
<html>
<script runat=server Language="C#">
DataView dw;
DataSet ds;
public void Page_Load(Object sender, EventArgs e)
{ string s1="Provider=Microsoft.Jet.OLEDB.4.0;";
s1+="Data Source=D:\\ASP\\DepotI.mdb";//Data Source两词之间有空格
string s2="SELECT * FROM Goods WHERE gID<3";
OleDbConnection conn=new OleDbConnection(s1);
OleDbDataAdapter da=new OleDbDataAdapter(s2,conn);
ds=new DataSet();
da.Fill(ds,"MyTable");
grid.DataSource=ds.Tables["MyTable"];
grid.DataBind();
}
public void HandleCommands(Object sender, DataGridCommandEventArgs e)
{ if(e.CommandName=="BuyButton")//判断是哪一个按钮发的事件
{ string s=e.Item.Cells[1].Text;//得到商品名称
int Num=1;//将要存入Cookie的值,初值为1
HttpCookie myCookie=Request.Cookies[s];//得到主键为s的Cookie
DateTime now = DateTime.Now;//得到当前时间
if(myCookie!=null)//判断有无主键为s的Cookie
{ Num=Convert.ToInt16(myCookie.Value);
Num++;//如果有,购买数量加1
}
else
{ myCookie=new HttpCookie(s);
}//如果没有,创建主键为s的Cookie,值为1
myCookie.Value=Num.ToString();//Cookie只存字符串
myCookie.Expires=now.AddHours(1);//存的数据1小时以后无效
Response.Cookies.Add(myCookie);// Cookies中的值不能修改,只能覆盖
HttpCookieCollection myCookieS=Request.Cookies;
s="";//将用户购买的商品全部显示在出来
if((myCookie=myCookieS["香蕉"])!=null)
s+="香蕉"+myCookieS["香蕉"].Value.ToString()+"<br>";
if((myCookie=myCookieS["苹果"])!=null)
s+="苹果"+myCookieS["苹果"].Value.ToString()+"<br>";
if((myCookie=myCookieS["菊花"])!=null)
s+="菊花"+myCookieS["菊花"].Value.ToString()+"<br>";
if((myCookie=myCookieS["茉莉"])!=null)
s+="茉莉"+myCookieS["茉莉"].Value.ToString()+"<br>";
label1.Text=s;
}
}
</script>
<body>
<form runat=server>
<asp:DataGrid id="grid" runat="server" GridLines="both"
OnItemCommand="HandleCommands" AutoGenerateColumns="false">
<columns>
<asp:BoundColumn runat="server" DataField="gID" HeaderText="编号"/>
<asp:BoundColumn runat="server" DataField="gName" HeaderText="货物名称"/>
<asp:BoundColumn runat="server" DataField="gNum" HeaderText="数量"/>
<asp:ButtonColumn runat="server" Text="购买" CommandName="BuyButton"/>
</columns>
</asp:DataGrid><br>
购物筐<br>
<asp:Label id=label1 Text="没有选择货物" runat="server" /><br>
<asp:HyperLink id="hyperlink1" Target="_self "
NavigateUrl="e11_3_2B.aspx" Text="选择花卉" runat="server"/>
</form>
</body>
</html>
例子e11_3_2B:第二个网页显示花卉,它和第一个网页文件只有2条语句不同(背景为黑色的语句),即Page_Load方法的string s2="SELECT * FROM Goods WHERE gID>2"和<asp:HyperLink id="hyperlink1" Target="_self " NavigateUrl="e11_3_2A.aspx" Text="选择水果" runat="server"/>,其余完全一样,这里就不列出第二个网页文件了。其实,本例根本不必用两个网页,一个网页完全能实现以上功能,因此也不必使用Cookie,这里只是为了说明Cookie的用法。请读者改为一个网页实现以上功能,如果不用Cookie又如何实现。
在浏览器中输入地址:http://Localhost/e11_3_2A.aspx,单击按钮选水果,单击1次,数量增加1个。转到第二个网页e11_3_2B.aspx,选花卉。在Label控件处显示所选择的商品。当然,本例只是说明问题,有许多不尽合理之处。
11.4 Application对象
当网站中的ASP.Net网页被第一次访问,网站Application对象被自动创建(网站只能有一个Application对象),如果已没有浏览器访问网站中的ASP.Net网页,Application对象被自动撤销,这个期间是Application对象的生存期。在Application对象中的变量也有相同生存期,并且这些变量可以被网站中的所有网页访问,因此这些变量是网站中所有网页的公用变量。由于存储在Application对象中的变量可以被所有网页读取,所以Application对象的变量也适合在网页之间传递信息。Application对象主要有以下用途:
l 存储记录在线人数或访问网站总人数的变量。
l 存储网站共用最新消息,供所有网页更新。
l 记录网站中各网页同一条广告被点击的次数或时间。
l 存储供所有网页使用的数据库数据。
l 不同用户之间通讯,例如多用户聊天室,多用户游戏等
本节首先介绍Application对象的用法,然后介绍记录访问网站总人数的实现方法。
11.4.1 Application对象方法和事件
这里只介绍Application对象常用的方法和事件。
l 方法Add:加入一个变量到Application对象中,例如Application.Add("string1","test"),表示向Application中加入一个名为string1的变量,其值为字符串"test",其实它的效果和Application("string1")="test"以及Application.item("string1")="test"是一样的。
l 方法Remove:从Application对象中删除变量,例如Application.Remove("string1")。
l 方法RemoveAll和Clear:清除Application对象中所有变量。
l 方法Get:使用名字变量名或者下标,来取得Application对象中变量值。例如object tmp= Application.Get("string1")或object tmp=Application.Get(0)。它等价于object tmp= Application("string1")或object tmp= Application(0)。
l 方法Set:修改Application对象中指定变量的值。例如Application.Set("string1","try")。等价于Application("string1")="try"。
l 方法GetKey:得到Application中指定下标的变量名。例如string s= Application.GetKey(0)。
l 方法Lock :该方法是用来解决多个用户对存储在Application中的同一变量进行修改时的同步问题。由于存储在Application对象中的变量可以被网站中的所有网页存取,为了避免多个用户同时修改同一变量发生错误,当一个用户在修改这个变量时,不允许其它用户修改。该方法阻止其他客户修改存储在Application对象中的变量,以确保在同一时刻仅有一个客户在修改Application变量。如果用户没有明确调用Unlock方法,则Web服务器将在修改变量的网页关闭后或锁定超时后,解除对Application对象的锁定。
l 方法Unlock:和Lock方法相反,Unlock方法将允许其它网页修改Application对象的变量。下例介绍一个修改计数器变量的方法。
Application.Lock;
Application["counter"]=(Int32)Application["counter"]+1;
Application.UnLock;
l 事件Application_OnStart:当网站中的ASP.Net网页被第一次访问时,产生的事件。
l 事件Application_OnEnd:没有浏览器访问网站中的ASP.Net网页后,产生的事件。这两个事件的事件处理函数必须写在global.asax文件之中。
11.4.2 Global.asax文件
Global.asax文件位于Web应用程序项目所在的Web应用目录下,Web应用目录概念见9.3.7节。每个解决方案中每个项目中都可以有一个Global.asax文件。使用VS.Net创建一个项目,将在Web应用目录中自动建立Global.asax文件,读者可以查看该文件的具体内容,这里就不列出了。用户使用浏览器不能下载或查看这个文件的内容。Global.asax文件实际上是一个可选文件,删除它不会出问题,当然是在没有使用它的情况下。在Global.asax 文件中定义了一个HttpApplication 类的派生类Global,在Global类中可以定义变量、方法、事件处理函数。在类中已预先定义了若干事件的事件处理函数,包括以下事件:Application_Start、Application_End、Application_BeginRequest、Application_EndRequest、Session_Start、Session_End等,还可以增加其它事件处理函数。在Global类中也可以放置一些组件,例如放置组件OleDbAdapter或SqlDataAdapter,也支持可视化设计,例如可以从"工具箱"的"数据"选项卡中,将 OleDbDataAdapter 对象拖到窗体上。"数据适配器配置向导"启动,它将帮助您创建连接和适配器。具体步骤可参见例子e8_12。这些组件可以供所有网页使用。
11.4.3 显示访问网站总人数的例子
例子e11_4_3:本例记录并显示建站以来访问网站的总人数。在Application对象中增加一个变量AllVister,记录访问网站的总人数。一个用户访问网站首先产生Session_Start事件,在此事件函数中,AllVister加1。用VS.Net实现的具体步骤如下:
(1) 创建一个Web应用项目,项目名为e11_4_3。
(2) 在global.asax文件中的Application_OnStart事件处理函数中增加语句如下:
Application.Add("AllVister",0);
(3) 在global.asax文件中的Session_Start事件处理函数中增加语句如下:
Application.Lock();
Application["AllVister"]=(int)Application["AllVister"]+1;
Application.UnLock();
(4) 单击VS.Net菜单"文件"|"添加新项(w)…"菜单项,打开"添加新项"对话框,在右侧选中"Web窗体"模板,名称为:WebForm2.aspx,单击"打开"按钮,创建WebForm2窗体。
(5) 放Label和HyperLink控件到WebForm1窗体,HyperLink控件NavigateUrl属性为WebForm2.aspx, 属性Text为"转到WebForm2网页"。
(6) WebForm1的Page_Load方法中增加如下语句:
void Page_Load(Object src,EventArgs e)
{ if(!Page.IsPostBack)//如果网页响应事件后刷新,计数器不加1
{ Application.Lock();
int num=(int)Application["AllVister"];
Application.UnLock();
Label1.Text="您是第"+Convert.ToString(num)+ "位访问者";
}
}
(7) 放Label和HyperLink控件到WebForm2窗体,HyperLink控件NavigateUrl属性为WebForm1.aspx, 属性Text为"转到WebForm1网页"。
(8) WebForm2的Page_Load方法中增加如下语句:
void Page_Load(Object src,EventArgs e)
{ if(!Page.IsPostBack)//如果网页响应事件后刷新,计数器不加1
{ Application.Lock();
int num=(int)Application["AllVister"];
Application.UnLock();
Label1.Text="您是第"+Convert.ToString(num)+ "位访问者";
}
}
(9) 运行,在浏览器打开WebForm1网页,查看显示的计数器数值,单击刷新按钮,查看显示的计数器数值是否改变,单击超级链接,转到WebForm2网页,查看显示的计数器数值是否改变,单击超级链接,再转回WebForm1网页,查看显示的计数器数值是否改变。关闭所有网页,等待一段时间,再打开WebForm1.aspx网页,显示的计数器值从1开始,这是因为没有网页访问网站时,Web应用程序关闭,Application对象被自动撤销。在打开新网页,产生Application_OnStart事件,将counter置为0。为了解决此问题,可以建立一个文件,记录访问网站总人数,初值为0,Application_OnStart事件函数中,从文件取出已访问网站总人数,赋值给counter,Application_OnEnd事件函数中,将counter存到文件中。
(10) 单击VS.Net菜单"文件"|"添加新项(w)…"菜单项,出现"添加新项"对话框,在右侧选中"文本文件"模板,名称为:TextFile1.txt,单击"打开"按钮,创建新文件。打开该文件,键入字符’0’,保存文件后,关闭该文件。
(11) 修改global.asax文件中的Application_OnStart事件处理函数语句如下:
string s=Server.MapPath("TextFile1.txt");
Application.Add("counterFile",s);//保存变量在Application_OnEnd事件函数使用
System.IO.StreamReader r=new System.IO.StreamReader (s);
s=r.ReadLine();
r.Close();
Application.Add("AllVister",Convert.ToInt32(s));
(12) 在global.asax文件中的Application_ OnEnd事件处理函数中增加语句如下:
//此时Server对象已不存在,无法用Server对象得到counter_File文件绝对路径
string s=(string)Application["counterFile"];//取出保存的文件的全路径地址
System.IO.StreamWriter w=new System.IO.StreamWriter (s,false);//建立新文件
int num=(int)Application["AllVister"];
w.Write(num.ToString());
w.Close();
(13) 再一次访问WebForm1.aspx网页,看是否已解决以上提出的问题。
11.5 Session对象
用浏览器访问一个网站,从网站的一个网页跳转到另一个网页,有时希望为这些被访问的网页中的数据建立某种联系,例如一个网上商店的购物筐,要记录用户在各个网页中所选的商品。前边用Cookie实现了购物筐。用Session对象也可以解决类似问题。
当用户使用浏览器进入网站访问网站中的第一个网页时,Web服务器将自动为该用户创建一个Session对象,在Session对象中可以建立一些变量,这个Session对象和Session对象中的变量只能被这个用户使用,其它用户不能使用。当用户浏览网站中的不同网页时,Session对象和存储在Session对象中的变量不会被清除,这些变量始终存在。当浏览器离开网站或超过一定时间和网站没有联系,Session对象被撤销,同时存储在Session中的变量也不存在了。用在Session对象中建立的变量的方法,可以在网页之间传递数据。
在ASP中,Session对象的功能本质上是用Cookie实现的,如果用户将浏览器上面的Cookies设置为禁用,那么Session就不能工作。但在ASP.Net中,如在config.web文件中,将<sessionstate cookieless="false" />设置为true,不使用Cookies,Session也正常工作。
11.5.1Session对象的属性、方法和事件
这里只介绍Session对象常用的方法和事件。
l 属性Count:在Session对象中建立的变量的项数。只读属性。
l 属性Keys:Session对象中的变量名也叫键,该属性得到所有键的集合。只读属性。
l 属性Mode:Session对象运行模式(只读),有4种模式,InProc:默认值,Session数据被保存在Web服务器的内存中。Off:Session对象被禁用。SQLServer:使用SQL Server数据库存储Session数据。StateServer:将Session数据存储在远程服务器上。
l 属性TimeOut:Session对象超时时限(分钟为单位)。如果用户在该超时时限之内不刷新或请求网页,则该用户的Session对象将终止。默认值是 2 0 分 钟。
l 方法Abandon:删除Session对象中所有的变量并释放Session对象的资源。如果未明确地调用Abandon方法,一旦超过属性TimeOut指定时间,服务器将删除Session对象。
l 方法Add:加入一个变量到Session对象中,例如Session.Add("string1","test"),表示向Session中加入一个名为string1的变量,其值为字符串"test",其实它的效果和Session("string1")="test"以及Session.item("string1")="test"是一样的。
l 方法RemoveAll 和Clear:清除Session对象中所有变量。
l 事件Session_OnStart:当用户使用浏览器进入网站访问网站中的第一个网页时,发生Session_OnStart事件。服务器在响应请求页之前先执行该事件处理函数。在该事件处理函数中可以判断用户是否登录,或判断是否首先访问了主页,这可用判断是否定义了某Session变量来实现,如果答案为否,可用ResPone.Redirect转向登录网页或主页。
l 事件Session_OnEnd:当浏览器离开网站,或超过属性TimeOut指定时间没有请求或刷新网站中的任何网页,该事件在Session对象被撤销前发生。
11.5.2用Session对象实现网上商店购物筐
本例用Session对象实现网上商店购物筐。由于数据库DepotI的goods表要被网站中所有网页使用,因此在Application_OnStart事件处理函数中,建立表goods的两个视图,一个是所有水果的视图,一个是所有花卉的视图,并把视图类引用变量存到Application中,这样视图类引用变量在整个Web应用程序运行期间都有效,在内存中的视图类对象就不会被垃圾收集器撤销,每个网页都可以直接使用这些视图类对象。购物筐也采用一个表,其字段和表goods相同,其中数量记录用户购买的数量。这个表在Session_Start事件处理函数中建立,表类的引用变量存到Session中,表生命周期从用户访问开始,到用户离开网站结束。水果和花卉分别用两个网页显示,每个网页中都有两个DataGrid控件,一个显示水果或花卉,另一个显示用户选择的商品的编号、货物名称、所选商品的数量,即显示购物筐中所选商品。用VS.Net实现的具体步骤如下:
(1) 创建一个Web应用项目,项目名为e11_5_2。
(2) 双击VS.Net集成环境右侧的解决方案管理器中的global.asax文件,打开global窗体,右击global窗体,在弹出快捷菜单中单击"查看代码"菜单项,打开global.asax源文件。在global.asax文件头部增加语句:using System.Data;using System.Data.OleDb;在global.asax文件中的Application_OnStart事件处理函数中增加语句如下:
string s="Provider=Microsoft.Jet.OLEDB.4.0;";
s+="Data Source=D:\\ASP\\DepotI.mdb";//Data Source两词之间有空格
OleDbConnection conn=new OleDbConnection(s);
s="SELECT * FROM Goods WHERE gID<3";
OleDbDataAdapter da=new OleDbDataAdapter(s,conn);
DataSet ds=new DataSet();
da.Fill(ds,"Table1");//Table1是只有水果的表
s="SELECT * FROM Goods WHERE gID>2";
da=new OleDbDataAdapter(s,conn);
da.Fill(ds,"Table2");//Table2是只有花卉的表
DataView dw1=new DataView(ds.Tables["Table1"]);//水果视图
DataView dw2=new DataView(ds.Tables["Table2"]);//花卉视图
Application["dw1"]=dw1;//保存两个视图类引用变量
Application["dw2"]=dw2;
(3) 在global.asax文件中的Session_Start事件处理函数中增加语句如下:
DataView dw1=(DataView)Application["dw1"];//2个dw1引用同一个视图类对象
DataTable dt=dw1.Table.Clone();//创建一个空表,字段和goods表相同
Session["dt"]=dt;//此表作为购物筐,在用户访问网站期间一直可用
(4) 在WebForm1类中定义DataView类和DataTable类变量:
DataView dataView1;DataTable DataTable1;
(5) 在WebForm1窗体中放置两个DataGrid控件,其属性Name分别为DataGrid1、DataGrid2。
(6) 为Page_Load事件处理函数增加语句:
private void Page_Load(object sender, System.EventArgs e)
{ dataView1=(DataView)Application["dw1"];//dataView1引用水果视图类对象
DataGrid1.DataSource=dataView1;//DataGrid1显示水果商品
DataGrid1.DataBind();//数据绑定
DataTable1=(DataTable)Session["dt"];//DataTable1引用购物筐
DataGrid2.DataSource=DataTable1.DefaultView;//DataGrid2显示购物筐中商品
DataGrid2.DataBind();
}
(7) 右击控件DataGrid1,在弹出快捷菜单中单击"属性生成器"菜单项,打开"DataGrid属性"对话框。在对话框左侧选中"常规",如图10.9C,将标题为"数据源(D):"的ComboBox控件置为空。选中"显示页眉"、"显示页脚"多选框,不选中"允许排序"多选框。在"DataGrid属性"对话框左侧选中"列",如图10.9D,不选中"在运行时自动创建列"多选框,在"可用列(A)"列表框中选中绑定列,单击标题为">"的按钮,将其移到"选定的列(S)"列表框,增加一个绑定列,页眉为"编号",数据字段为gID;用同样的办法增加另外2个绑定列,页眉分别为"货物名称"、"货物数量",数据字段分别为gName、gNum。全部为只读列。在"可用列(A)"列表框中单击"按钮列"前的+号,展开树,可以看到选择项,选中选择项,单击标题为">"的按钮,增加一个按钮列,页眉为"单击按钮购买",Text属性为"购买",命令名为:BuyBtn,按钮类型为LinkButton。
(8) 为按钮列增加事件ItemCommand的事件处理函数如下:
private void DataGrid1_ItemCommand(object source,
System.Web.UI.WebControls.DataGridCommandEventArgs e)
{ string s="gID="+e.Item.Cells[0].Text;//DataGrid1当前行第0列文本,即货物编号
DataRow[] foundRows=DataTable1.Select(s);//查找购物筐中是否有此编号商品
if(foundRows.Length==0)//购物筐中没有此编号商品,在记录购物的表中增加新记录
{ DataRow dr=DataTable1.NewRow();//创建DataTable1表新记录
s=e.Item.Cells[0].Text;//DataGrid1当前行第0列文本,即货物编号
dr["gID"]=Convert.ToInt16(s);//新纪录的gID=货物编号
dr["gName"]=e.Item.Cells[1].Text;//DataGrid1当前行第1列文本,即货物名称
dr["gNum"]=1;//购买数量为1
DataTable1.Rows.Add(dr);//DataTable1表增加新记录
}
else//购物筐中已有所选编号商品,在相应记录中数量字段加1
{ object o=foundRows[0]["gNum"];//原购买商品数量
s=o.ToString();//用int n=foundRows[0]["gNum"]不能通过
int n=Convert.ToInt16(s);
n++;//购买商品数量加1
foundRows[0]["gNum"]=n;
}
DataGrid2.DataBind();
}//还应该将所选货物的存量减1,如付款购买,源数据库也应修改,如不买要恢复原数据
(9) 右击DataGrid2,在弹出快捷菜单中单击"属性生成器"菜单项,打开"DataGrid属性"对话框。在对话框左侧选中"常规",如图10.9C,将"数据源(D):"的ComboBox控件置为空。选中"显示页眉"、"显示页脚"多选框,不选中"允许排序"多选框。在"DataGrid属性"对话框左侧选中"列",如图10.9D,不选中"在运行时自动创建列"多选框,在"可用列(A)"列表框中选中绑定列,单击标题为">"的按钮,将其移到"选定的列(S)"列表框,增加一个绑定列,页眉为"编号",数据字段为gID;用同样的办法增加另外2个绑定列,页眉分别为"货物名称"、"购买数量",数据字段分别为gName、gNum。
(10) 放HyperLink控件到窗体,属性Text="选择花卉",NavigateUrl属性为WebForm2.aspx。
(11) 单击VS.Net菜单"文件"|"添加新项(w)…"菜单项,出现"添加新项"对话框,在右侧选中"Web窗体"模板,窗体名为:WebForm2.aspx,单击"打开"按钮,创建新窗体WebForm2。
(12) 按照为WebForm1窗体增加控件、变量和方法的步骤,为WebForm2窗体增加控件和变量,只是在第6步中,Page_Load事件处理函数增加语句dataView1=(DataView1) Application["dw1"]修改为dataView1=(DataView1)Application["dw2"]。在第10步中,控件HyperLink的NavigateUrl属性为WebForm1.aspx,属性Text="选择水果"。
(13) 在浏览器中输入地址:http://Localhost/WebForm1.aspx,选中某种水果,转到第二个网页WebForm2.aspx,选中某种花卉,购物筐中应显示所选的所有商品。本例不尽合理,读者可以以此为基础修改,创建网上商店。
11.6 Server对象
Server对象提供对Web服务器资源进行访问的方法,主要包括:得到服务器的计算机名称,设置脚本程序失效时间,将HTML的特殊标记转变为ASCII字符,得到文件的真实路径等,本节将逐一介绍这些方法。使用Server 对象也可以从一个网页传递数据到另一个网页。
11.6.1 Server对象属性和方法
这里只介绍Server对象常用的方法和事件。
l 属性MachineName:该属性用来获取当前运行Web应用程序的Web服务器的计算机名称,使用方法如下:string s=Server.MachineName;这个计算机名称可以用如下办法查到:打开"控制面板",选中"系统"中的"计算机名",应和用Server对象的属性MachineName获得计算机名称一致。
l 属性ScriptTimeout:Web应用程序由于运行在计算机网络中,由于网络的原因,一些代码可能无法完成,一直在等待,这将极大消耗Web服务器的资源,为了避免这种情况,可以设置程序运行的最长时间,即设置属性ScriptTimeout,在脚本程序运行超过属性ScriptTimeout指定时间之后即作超时处理,也就停止程序运行。如以下代码指定服务器处理脚本程序在100秒后超时:Server.ScriptTimeout=100,其默认值为90秒。
l 方法HtmlEncode和HtmlDecode:HTML标记语言中,有些ASCII字符被作为标记,例如字符串:<br>中的<和>都是标记,如需要显示这些字符,必须作特殊处理,例如为了在浏览器中正确显示如下字符串:"<br>是换行标记",字符串必须写为如下形式:
<asp:Label id="label1" Text="%3cbr%3c是换行标记" runat=server></asp:Label>;
也可以用Server对象的属性HtmlEncode方法,用法如下:
<asp:Label id="label1" runat=server>Server.HtmlEncode(”<br>是换行标记”)</asp:Label>;
方法HtmlDecode对被HtmlEncode方法编码的字符串进行解码。例如:
string s=Server.HtmlDecode(label1.Text);
l 方法URLEncode和UrlDecode:在URL中,像?、&、/ 和空格这样的字符有特殊意义,因此这些字符在URL中不能作为普通字符使用,用HttpUtility.UrlEncode方法将对具有特殊含义字符做特殊处理。确保所有浏览器均正确地传输 URL 字符串中的文本,见例子e11_2_1C。方法UrlDecode对字符串进行URL解码并返回已解码的字符串,例如String s= Server.UrlDecode(已编码字符串);
l 方法MapPath:网页中网页文件的路径一般是以宿主目录为根目录,不同的系统中,宿主目录所在的实际目录并不相同,而且网页也可能在虚拟目录中。因此网页文件的路径并不是网页文件的实际路径。而在用File类处理文件时,则要求文件的地址必须是实际的全路径,Server对象的MapPath方法提供这两种路径的转换方法,例如,f1.aspx文件存在宿主目录下的Test目录下,用Server对象得到f1.aspx文件绝对路径方法如下:
string s=Serve.MapPath(\Test\f1.aspx);//这里\表示以宿主目录
也可以用如下语句:
string s=Serve.MapPath(Test\f1.aspx);//表示单前网页所在的目录的子目录Test
l 方法Transfer:终止当前网页,转向参数指定的URL路径的一个新页。例子见下节。
11.6.2使用Transfer在网页之间传递数据
11.2.1节介绍了用Request和Response对象在网页之间传递数据,本节介绍使用Transfer在网页之间传递数据。
例子e11_6_2A:创建一个Web网页,将数据发送到另一个Web网页,网页文件如下:
<%@ Page Language="C#" ClassName="FirstPageClass" %>
<html>
<script runat="server">
public string Data1
{ get
{ return textBox1.Text; }
}
void ButtonClicked(object sender, EventArgs e)
{ Server.Transfer("e11.6.2B.aspx"); }
</script>
<body>
<form runat="server">
输入数据:<asp:TextBox id="textBox1" runat="server"/> <br>
<asp:Button OnClick="ButtonClicked" Text="单击转向第2个网页" runat=server />
</form>
</body>
</html>
在网页的顶部的@Page指令中,ClassName属性设置本网页有效的类名,类名由程序员定义。然后为要传递到另一个网页的每个值都定义一个具有get访问器的属性,get访问器返回要传递的值,本例是Web窗体的文本框输入的值。必须在服务器端脚本中定义这些属性。当单击了按钮后,要将数据传递到另一个网页时,在按钮的事件处理行数中使用 Server.Transfer("e11_6_2B.aspx")语句,转向e11_6_2B.aspx,同时传递数据。
例子e11_6_2B:创建一个Web网页,接受另一个Web网页传递的数据,网页文件如下:
<%@ Page Language="C#" %>
<%@ Reference Page="e11.6.2A.aspx" %>
<html>
<script runat="server">
FirstPageClass fp;
void Page_Load()
{ if (!IsPostBack)
{ fp= (FirstPageClass)Context.Handler;
Label1.Text=fp.Data1;
}
}
</script>
<body>
<form runat="server">
你好:<asp:Label id="Label1" runat=server>
</form>
</body>
</html>
在网页的顶部增加指令:<%@ Reference Page="e11.6.2A.aspx" %>,其中Page属性值为e11.6.2A.aspx网页。在服务器端脚本中声明变量:FirstPageClass fp,fp将引用发送信息的网页中定义的类的实例。Page_Load 事件处理程序中,用语句fp=(FirstPageClass) Context.Handler引用这个对象。用fp.Data1得到对象中的属性值。
例子e11_6_2C:用VS.Net实现上述功能,具体步骤如下:
(1) 创建一个Web应用项目,项目名为e11_6_2C。
(2) 在WebForm1窗体中放置TextBox控件,Name属性textBox1。
(3) 在WebForm1类中增加属性Data1
public string Data1
{ get
{ return textBox1.Text; }
}
(4) 在WebForm1窗体中放置Button控件,单击事件处理函数如下:
void button1_Click(object sender, EventArgs e)
{ Server.Transfer("WebForm2.aspx"); }
(5) 单击VS.Net菜单"文件"|"添加新项(w)…"菜单项,出现"添加新项"对话框,在右侧选中"Web窗体"模板,窗体名为:WebForm2.aspx,单击"打开"按钮,创建新窗体WebForm2。
(6) 在WebForm2窗体中放置Label控件,Name属性Label。
(7) 为WebForm2类增加变量public WebForm1 fp;这里WebForm1是在文件WebForm1.aspx定义的类名。
(8) 在Page_Load()增加语句如下:
void Page_Load()
{ if (!IsPostBack)
{ fp = (WebForm1)Context.Handler;
label1.Text=fp.Data1;
}
}
(9) 在WebForm2.aspx 文件Page语句后增加语句:<%@ Reference Page="WebForm1.aspx" %>
(10) 运行,打开WebForm1.aspx,在textBox1输入数据,单击按钮,打开WebForm2.aspx,在其label1中显示输入的数据。
11.7 Cache对象
Cache对象生存期和Application对象生存期一样长,因此,也可以在Cache对象中建立一些网站中所有网页都可使用的公用变量。例如,在Cache对象中增加一个DataSet类变量用语句:Cache["myDataSet"]=DataSet1; 取出DataSet类变量用语句:DataSet dataSet1=(DataSet)Cache["myDataSet"];
和Application对象不同,在Web服务器内存比较紧张时,为了提高Web服务器的性能,Cache对象采用最近最少使用(LRU)方法自动清除不常用的变量。因此每次取出Cache对象中的变量,要检查一下是否为NULL,如果是NULL,则要重新建立DataSet对象。
11.8 Config.web配置文件
ASP.Net的配置文件是基于XML格式的纯文本文件,保存在Web应用目录下,统一命名为"config.web"。它决定了所在目录及其子目录的配置信息。在子目录中可以增加Config.web配置文件,并且子目录下的配置覆盖其父目录的配置。在操作系统安装目录\Microsoft.Net\Framework\版本号\下的config.web为整个机器的根配置文件,它定义了整个环境下的缺省配置。缺省情况下,浏览器是不能够直接访问目录下的config.web文件。在运行状态下,ASP.Net会根据远程URL请求,把访问路径下的各个config.web配置文件叠加,产生一个唯一的配置集合。举例来说,一个对URL: http://localhost\webapp\owndir\test.aspx的访问,ASP.Net会根据以下顺序来决定最终的配置情况:
1..\Microsoft.Net\Framework\v.1.00\config.web (缺省配置文件)
2..\webapp\config.web (应用的配置)
3..\webapp\owndir\config.web (自己的配置)
ASP.Net提供了一个丰富而可行的配置系统,以帮助管理人员轻松快速的建立自己的WEB应用环境。ASP.Net提供的是一个层次配置架构,可以帮助WEB应用、站点、机器分别配置自己的扩展配置数据。ASP.Net的配置系统具有以下优点:
l ASP.Net允许配置内容可以和静态内容、动态页面和商业对象放置在同一应用的目录结构下。当管理人员需要安装新的ASP.Net应用时,只需要将应用目录拷贝到新的机器上即可。
l ASP.Net的配置内容以纯文本方式保存,可以以任意标准的文本编辑器、XML解析器和脚本语言解释、修改配置内容。
l ASP.Net 提供了扩展配置内容的架构,以支持第三方开发者配置自己的内容。
l ASP.Net配置文件的更新被系统自动监控,无须管理人员手工干预。
VS.Net为每一个Web应用项目自动建立了一个config.web文件如下,它是一个XML文件,这里只对XML文件的注解做了修改,请读者仔细研究该文件,理解其意义。
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
<!-- 动态调试编译:defaultLanguage="c#"表示本网页使用的默认语言。设置debug="true"启用 ASPX调试,这样设置将一些调试符号插入到编译页中,这将创建执行起来较慢的大文件,因此应该只在调试时将此值设置为true;设置为debug="false"将没有调试功能,但将提高应用程序的运行时性能。调试完成后,应设置debug="false"。有关更多信息,请参考有关调试 ASP.Net 文件的文档。 -->
<compilation defaultLanguage="c#" debug="true" />
<!-- 自定义错误信息(customErrors):网页被浏览时可能发生错误,系统可用默认错误网页显示发生错误的详细信息。在创建网页时这些信息对改正错误很有帮助,但在网页被用户浏览时显示默认错误网页是不合适的,此时可用defaultRedirect属性指定显示错误信息的网页的URL(本文件未设置该属性),这个网页被称为:自定义默认错误网页。属性mode="On"将仅用自定义默认错误网页显示错误信息;属性mode= "Off"将仅用默认错误网页显示错误信息;属性mode="RemoteOnly"对于和Web 服务器不在同一台计算机的用户,用自定义默认错误网页显示错误信息,对于和Web 服务器在同一台计算机的用户,用默认错误网页显示错误信息。出于安全目的,建议使用此设置,以便不向远程客户端显示应用程序的详细信息。 -->
<customErrors mode="RemoteOnly" />
<!-- 身份验证:此节设置应用程序的身份验证策略。可能的模式是 "Windows"、"Forms"、"Passport" 和 "None"。"None" 不执行身份验证。"Windows" IIS 根据应用程序的设置执行身份验证(基本、简要或集成 Windows)。在 IIS 中必须禁用匿名访问。"Forms" 您为用户提供一个输入凭据的自定义窗体(Web 页),然后在您的应用程序中验证他们的身份。用户凭据标记存储在 Cookie 中。"Passport" 身份验证是通过 Microsoft 的集中身份验证服务执行的,它为成员站点提供单独登录和核心配置文件服务。-->
<authentication mode="Windows" />
<!-- 授权:此节设置应用程序的授权策略。可以允许或拒绝不同的用户或角色访问应用程序资源。通配符: "*" 表示任何人,"?" 表示匿名(未经身份验证的)用户。 -->
<authorization>
<allow users="*" /> <!-- 允许所有用户 -->
<!-- <allow users="[逗号分隔的用户列表]" roles="[逗号分隔的角色列表]"/>
<deny users="[逗号分隔的用户列表]" roles="[逗号分隔的角色列表]"/>
-->
</authorization>
<!-- 应用程序级别跟踪记录:应用程序级别跟踪为应用程序中的每一页启用跟踪日志输出。设置 trace enabled="true" 可以启用应用程序跟踪记录。如果 pageOutput="true",则在每一页的底部显示跟踪信息。否则,可以通过浏览 Web 应用程序根目录中的 "trace.axd" 页来查看应用程序跟踪日志。 -->
<trace enabled="false" requestLimit="10" pageOutput="false"
traceMode="SortByTime" localOnly="true" />
<!-- 会话状态设置:默认情况下,ASP.Net使用Cookie来标识哪些请求属于特定的会话。如果 Cookie不可用,则可以通过将会话标识符添加到 URL 来跟踪会话。若要禁用 Cookie,请设置essionState cookieless="true"。 -->
<sessionState mode="InProc" stateConnectionString="tcpip=127.0.0.1:42424"
sqlConnectionString="data source=127.0.0.1;Trusted_Connection=yes"
cookieless="false" timeout="20" />
<!-- 全球化:此节设置应用程序的全球化设置。 -->
<globalization requestEncoding="utf-8" responseEncoding="utf-8" />
</system.web>
</configuration>
在config.web文件中除了以上设置外,还可以增加自定义标记,用来存储一些在运行中不必修改的数据,例如数据库连接字符串,当把数据库位置移动时,只需修改config.web文件中相关设置。在网页文件中可以用第12章介绍的读写XML文件的方法将有关的设置读出。下边是一个例子:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
<appSettings>
<add key=="数据库路径" value=="Provider=Microsoft.Jet.OLEDB.4.0;Data
Source=D:\\vc#\\studentI.mdb"/>
</appSettings>
</system.web>
</configuration>
习题
(1) 如何实现记录访问网站的在线人数。(提示:在Session_End事件函数中计数器减1。)
(2) 用Application对象建立一个2人聊天室。如果是多人聊天室,又如何实现。
(3) 如何防止用户不经过主页或企图不经过登录直接访问其它网页。
(4) 例子e11_3_2中从一个网页转到另一网页时,购物筐显示不正确,请修改。
(5) 例子e11_3_2和e11_5_2完全可使用一个网页,请问如何实现。
(6) 例子e11_5_2中,也可以把DataSet类变量存到Application中,请问如何实现。
(7) 例子e11_3_1中,单击刷新按钮,访问次数也加1,这不合理,如何禁止。
(8) 修改例子e11_5_2,不使用Application对象,改用Cache对象。
(9) 总结一下,从一个网页向另一个网页传递数据的方法。
(10) 创建一个网上书店,具有登录、注册功能,能查询指定书籍,用网页返回,具有购物筐功能,选中商品放入后,显示的商品数量减少,购物后,原数据商品数量也要减少。
第二章 可扩展标记语言
本章介绍XML可扩展标记语言的基本概念和使用,包括使用XML的必要性、XML定义,以及如何建立、显示和处理XML文档数据,XML数据和数据库数据之间的转换等。
12.1 XML可扩展标记语言的基本概念
XML是基于文本的标记语言,它通过有意义的标签以结构化的格式存储数据,这种格式可以被任何一种计算机系统所解释。本节介绍XML的基本概念。
12.1.1 HTML及其缺点
Internet提供了全球范围的网络互连与通信功能,Web技术的发展更是一日千里,其丰富的信息资源给人们的学习和生活带来了极大的便利。特别是应运而生的HTML(超文本标记语言),以简单易学、灵活通用的特性,使人们发布、检索、交流信息都变得非常简单,从而使Web成了最大的环球信息资源库。然而,电子商务、电子出版、远程教育等基于Web的新兴领域的全面兴起,使得传统的Web资源更加复杂化、多样化,人们对Web服务功能的需求也达到更高的标准。而传统的HTML由于自身特点的限制,不能满足这些要求。HTML主要有如下不足:
l HTML的标记都是预先定义的,用户不能自定义有意义的标记,可扩展性差。
l HTML的显示方式内嵌在数据中,这样在创建文本时,要同时考虑显示格式,如果因为需求不同而需要对同样的内容进行不同风格的显示时,要从头创建一个全新的文档,重复工作量很大。不能对数据按照不同的需求进行多样化显示。
l HTML缺乏对数据结构的描述,对于用程序理解文档内容、抽取语义信息都有诸多不便。不能进行智能化的语义搜索。不能对不同平台、不同格式的数据源进行数据集成和数据转化等。
l HTML语言不能描述矢量图形、数学公式、化学符号等特殊对象。
12.1.2 SGML(标准通用标记语言)
SGML(Standard Generalized Markup Language)是一种通用的文档结构描述标记语言,为文档数据的标记提供了异常强大的工具,同时具有极好的扩展性,因此在数据分类和索引中非常有用。但SGML复杂度太高,不适合网络的日常应用,加上开发成本高、不被主流浏览器所支持等原因,使得SGML在Web上的推广受到阻碍。
12.1.3 XML(可扩展标记语言)
XML(eXtensible Markup Language)是由W3C于1998年2月发布的一种标准。它是SGML的一个简化子集,它将SGML的丰富功能与HTML的易用性结合到Web的应用中。XML的优点如下:
l XML简单易用,功能强大。
l XML允许各个组织、个人建立适合自己需要的标记集合,并且这些标记可以用通用的工具显示。例如定义数学、化学、音乐等专用标记。
l XML的最大优点在于它的数据存储格式不受显示格式的制约。一般来说,一篇文档包括三个要素:数据、结构以及显示方式。XML把文档的显示格式从数据内容中独立出来,保存在样式表文件(Style Sheet)中,这样如果需要改变文档的显示方式,只要修改样式表文件就行了。
l 通过有意义的标签以结构化的格式存储数据,用一种开放的自我描述方式定义数据结构,在描述数据内容的同时突出对结构的描述,从而体现出数据之间的关系,XML的自我描述性质能够很好地表现许多复杂的数据关系,使得基于XML的应用程序可以在XML文件中准确高效地搜索相关的数据内容,忽略其它不相关部分。
l XML还有其他许多优点,比如它有利于不同系统之间的信息交流,完全可以充当网际语言,并有希望成为数据和文档交换的标准机制。
由于以上优点,XML必将在商务的自动化处理,信息发布,智能化的Web应用程序和数据集成等领域被广泛使用。
12.1.4 XML的文档格式
首先介绍XML文档内容的基本单元——元素,它的语法格式如下:
〈标签〉文本内容〈/标签〉
元素是由起始标签、元素内容和结束标签组成。用户把要描述的数据对象放在起始标签和结束标签之间。例如:<姓名>王平</姓名>。无论文本内容有多长或者多么复杂,XML元素中可以再嵌套别的元素,这样使相关信息构成等级结构。用这样的方法定义XML文档和数据结构。
例子e12_1_4:下面的例子是一个描述学生情况的XML文档,在<学生>元素中包括了所有学生的信息,每个学生都由<学生>元素来描述,而<学生>元素中又嵌套了<编号>、<姓名>、<性别>和<年龄>元素。完整XML文件e12_1_4.xml内容如下:
<?xml version="1.0" encoding="GB2312"?>
<?xml-stylesheet type="text/xsl" href="e12_2_1.xsl"?>
<学生>
<编号>001</编号>
<姓名>张三</姓名>
<性别>男</性别>
<年龄>20</年龄>
</学生>
除了元素,XML文档中出现的有效对象是:声明、注释、根元素、子元素和属性。
l 声明:声明给XML解析器提供信息,使其能够正确解释文档内容,它的起始标识是"<?",结束标识是"?>"。例如XML声明:<?xml version="1.0" encoding="GB2312"?>,该声明指明使用的XML版本号和文档使用的字符集是中文字符集"GB2312"。又如显示样式表文件声明:<?xml-stylesheet type="text/xsl" href=" e12_2_1.xsl"?>,指明使用e12_2_1.xsl样式表文件显示本XML文档。
l 注释:注释是XML文件中用作解释的字符数据,XML处理器不对它们进行任何处理。注释文本被"<!--"和" -->"标记,注释可以出现在XML元素间的任何地方,但是不可以嵌套。下边是一个注释的例子:<!--这是一个注释-->。
l 根元素和子元素:如果一个元素从文件头的序言部分之后开始,一直到文件尾,包含了文件中所有的数据信息,我们称之为根元素。XML元素是可以嵌套的,那么被嵌套在内的元素称为子元素。在前面的例子中,<学生>就是根元素,<编号>就是<学生>的子元素。一个XML文档中有且仅有一个根元素,其他所有的元素都是它的子元素。
l 属性:属性给元素提供进一步的说明信息,它必须出现在起始标签中。属性以名称/值成对出现,属性名不能重复,名称与取值之间用等号分隔,取值用引号括起来。例如:<工资 currency="US$"> 25000 </工资>,上例中的属性说明了薪水的货币单位是美元。
l XML文档的基本结构:XML文档的基本结构由序言部分和一个根元素组成。序言包括了XML声明和DTD或XSD声明,DTD(Document Type Define,文档定义类型)和XSD(XML Schema,XML架构)都是用来描述XML文档的数据结构的。例如,在例子e12_1_4的文档前面加上如下的序言部分,就构成了一个完整的XML文档:
<?xml version="1.0" encoding="GB2312"?>
<?xml-stylesheet type="text/xsl" href="student1.xsl"?>
<!DOCTYPE employees SYSTEM"employees.dtd">
l 格式良好的(Well-Formed)XML文档:一个XML文档首先应当是格式良好的,格式良好XML文档的正式定义位于:http://www.w3.org/TR/REC-xml。格式良好的XML文档除了要满足根元素唯一的特性之外,还包括:
(1) 起始标签和结束标签应当匹配,结束标签是必不可少的。
(2) 大小写应一致,XML对字母的大小写是敏感的,<employee>和<Employee>是完全不同的两个标签,所以结束标签在匹配时一定要注意大小写一致。
(3) 元素应当正确嵌套,子元素应当完全包括在父辈元素中,下面的例子就是错误嵌套:<A> <B> </A> </B>,正确的嵌套方式如下:<A> <B> </B> </A> 。
(4) 属性值必须包括在引号中,元素中的属性名是不允许重复的。
12.1.5 用DTD和XML Schema定义XML架构
DTD(Document Type Definition 文档类型定义)是SGML语言的组成部分,可以用来定义XML文档的数据结构和组成结构的元素类型,可以看作一个或多个XML文档的模板。使用DTD可以对一个XML文档的结构进行校验。它可以是一个独立文件,也可以直接放在XML文档中。例如,例子e12_1_4的DTD文件如下:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE学生[
<!ELEMENT学生 (编号, 姓名, 性别, 年龄)>
<!ELEMENT编号 (#PCDATA)>
<!ELEMENT姓名 (#PCDATA)>
<!ELEMENT性别 (#PCDATA)>
<!ELEMENT年龄 (#PCDATA)>
]>
由于DTD采用了非XML的语法规则,不支持多种多样的数据类型,扩展性较差等原因,W3C提出了XML Schema(XML架构,XSD),在保留了并扩充了DTD原有的文档结构说明能力的同时,克服了DTD的缺点。XML Schema使用的例子见12.3.5节。
12.1.6 较复杂的XML文档
例子e12_1_6:为了说明属性的用法,以及为显示XML文档提供一个例子,这里建立一个较复杂的XML文档,有较多的数据。本例是一个描述书店中所有书籍的XML文档,显示了XML文档的各种元素用法,用记事本程序输入以下内容,网页文件如下:
<?xml version="1.0" encoding="GB2312" ?>
<!--这是一个注释-->
<bookstore>
<book 出版社="电子工业出版社">
<书名>SQL实用全书</书名>
<作者>Rafe Colburn</作者>
<出版日期>2001年6月</出版日期>
<价格>34.00</价格>
</book>
<book 出版社="清华大学出版社">
<书名>C#高级编程</书名>
<作者>Simon Robinson</作者>
<出版日期>2002年6月</出版日期>
<价格>128.00</价格>
</book>
<book 出版社="人民邮电出版社">
<书名>ASP.Net从入门到精通</书名>
<作者>Chris Payne</作者>
<出版日期>2002年1月</出版日期>
<价格>41.00</价格>
</book>
<book 出版社="中国青年出版社">
<书名>精通C#与ASP.Net程序设计</书名>
<作者>孙三才</作者>
<出版日期>2003年6月</出版日期>
<价格>39.00</价格>
</book>
<book 出版社="电子工业出版社">
<书名>ASP.Net实用全书</书名>
<作者>张三</作者>
<出版日期>2004年6月</出版日期>
<价格>55.00</价格>
</book>
</bookstore>
用IE浏览器(5.0以上版本)浏览e12_1_6.xml文件,效果如上图。单击标记前的减号(或加号),看一下效果。上图显示的数据,已用单击减号方法,将最后几本书的数据隐藏。
12.2 XML文档显示
由于XML文档只是定义数据及其数据结构,并不包含显示的格式。如要按指定格式显示这些数据,必须采用其它方法定义显示格式。本节介绍显示XML文档的一些方法。
12.2.1 用XSL文件显示XML文档
使用CSS文件或XSL文件可以定义XML文档的显示格式。这里使用两个XSL文件按不同显示格式显示同一个XML文件。
例子e12_2_1:首先定义第一个xsl文件e12_2_1.xsl显示e12_1_4.xml内容。文件如下:
<?xml version="1.0" encoding="GB2312"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:for-each select="学生">
<xsl:value-of select="编号"/>,
<xsl:value-of select="姓名"/>,
<xsl:value-of select="性别"/>,
<xsl:value-of select="年龄"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
将文件e12_2_1.xsl和e12_1_4.xml存到同一文件夹中。请注意e12_1_4.xml文件中的语句<?xml-stylesheet type="text/xsl" href="e12_2_1.xsl"?>,表示使用e12_2_1.xsl样式表文件显示e12_1_4.xml文件。用IE打开e12_1_4.xml文件,显示效果如上图。
例子e12_2_1B:定义第二个xsl文件e12_2_1B.xsl,以不同的显示方式显示e12_1_4.xml文件。文件如下:
<?xml version="1.0" encoding="GB2312"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<xsl:for-each select="学生">
<table border="1" cellpadding="0" cellspacing="0" bordercolor="#111111"
style="border-collapse:collapse" width="100%" id="AutoNumber1">
<tr>
<td width="50%">编号</td>
<td width="50%"> <xsl:value-of select="编号"/> </td>
</tr>
<tr>
<td width="50%">姓名</td>
<td width="50%"> <xsl:value-of select="姓名"/> </td>
</tr>
<tr>
<td width="50%">性别</td>
<td width="50%"> <xsl:value-of select="性别"/> </td>
</tr>
<tr>
<td width="50%">年龄</td>
<td width="50%"> <xsl:value-of select="年龄"/> </td>
</tr>
</table>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
将文件e12_2_1B.xsl和e12_1_4.xml存到同一文件夹,修改e12_1_4.xml文件的第2条语句为:<?xml-stylesheet type="text/xsl" href="e12_2_1B.xsl"?>。用IE打开e12_1_4.xml文件,显示效果如上图。
12.2.2 使用XML控件显示XML文档
例子e12_2_2:用XML控件也可以显示XML文档,XML控件属性DocumentSource是要显示的XML文件,属性TransformSource是指定显示格式的XSL文件。下边是使用XML控件显示e12_1_4.xml文件的例子。显示效果和例子e12_2_1B相同。
<%@ Page Language="C#" %>
<HTML>
<body>
<h3>使用Xml控件示例</h3>
<form runat="server" ID="Form1">
<asp:Xml id="xml1" DocumentSource="e12_1_4.xml"
TransformSource="e12_2_1B.xsl" runat="server" />
</form>
</body>
</HTML>
12.2.3 使用数据绑定方法显示XML文档
DataSet 类提供了若干方法处理XML文件,主要有:
l GetXml():将DataSet中数据转换为XML格式,以字符串类型返回。
l GetXmlSchema():将DataSet中数据转换为XML格式,以字符串类型返回其XSD架构。
l ReadXml():将包括XML架构和数据的XML文件读入DataSet。
l ReadXmlSchema():将XML架构(XSD)文件读入DataSet。
l WriteXml():将DataSet中数据转换为XML格式写入XML文件,可包含或不包含架构。
l WriteXmlSchema():将DataSet中数据转换为XML格式,将XML架构写入XML文件。
本节仅介绍将XML文档读入DataSet,其它例子见12.4节。
例子e12_2_3:XML文档也可以作为控件的数据源,本例使用e12_1_6.xml作为DataGrid控件的数据源,用DataGrid控件把XML文档显示出来。网页文件如下:
<%@ Import Namespace="System.Xml" %>
<%@ Import Namespace="System.Data" %>
<html>
<script runat=server Language="C#">
public void Page_Load(Object sender, EventArgs e)
{ DataSet ds = new DataSet();
ds.ReadXml(Server.MapPath("e12_1_6.xml"));
DataGrid1.DataSource=ds;
DataGrid1.DataMember="book";//将本XML数据看作数据库表book
DataGrid1.DataBind();
}
</script>
<body>
<h2>用数据绑定方法显示XML文档</h2>
<form runat=server>
<asp:DataGrid id="DataGrid1" runat="server"/>
</form>
</body>
</html>
网页的显示效果如上图。将XML文件读入DataSet后,就可以将此文件的内容看作一个数据库表,例如本例为"book",该表也可以用ds.Tables[0]表示,表名为:ds.Tables[0].TableName。
12.2.4 使用VS.Net建立和显示XML文档
例子e12_2_4:本例使用VS.Net建立网页文件,用来显示e12_1_6.xml文档
(1) 创建一个Web应用程序框架,项目名为e12_2_4。
(2) 在窗体中放置控件DataGrid,其属性Name=DataGrid1。
(3) 单击VS.Net菜单"项目"|"添加新项"菜单项,弹出标题为"添加新项"的窗口,在窗口中选中XML文件,文件名为MyXMLFile.xml,单击"打开"按钮,增加一个XML文件。
(4) 将XML文件e12_1_6内容拷贝到MyXMLFile.xml中。使用VS.Net创建XML文档,其XML文档编辑器为编写XML文档提供了一些支持,例如,当加入XML开始标记,将自动增加XML结束标记,并能以网格的形式显示数据。
(5) 单击MyXMLFile.xml窗口下的"数据"标签,可以看到用表格显示的XML文件。
(6) 为Page_Load事件函数增加语句:
private void Page_Load(object sender, System.EventArgs e)
{ DataSet ds = new DataSet();
ds.ReadXml(Server.MapPath("MyXMLFile.xml"));
DataGrid1.DataSource=ds;
DataGrid1.DataMember="book";
DataGrid1.DataBind();
}
(7) 运行,可以看到用表格显示的XML文件,显示效果和例子e12_2_3相同。
12.2.5 将XML文件转换为HTML文件
例子e12_2_5:本例将e12_1_4.xml文件,按照e12_2_1B.xsl定义的显示格式生成Html文件e12_2_5.htm。用浏览器IE显示效果和例子e12_2_1B相同。网页文件如下:
<%@ import namespace="System.Xml" %>
<%@ import namespace="System.Xml.XPath" %>
<%@ import namespace="System.Xml.Xsl" %>
<html>
<script language="c#" runat=server>
void Btn_Click(Object src,EventArgs e)
{ XmlDocument doc=new XmlDocument();//创建XmlDocument类的实例
doc.Load(Server.MapPath("e12_1_4.xml"));//读XML文件到内存,形成DOM结构
XPathNavigator nav=doc.CreateNavigator();//nav可以随机查询doc文档的节点
XslTransform Xslt=new XslTransform();//Xslt负责将XML文件转换为HTML文件
Xslt.Load(Server.MapPath("e12_2_1B.xsl"));//装入转换的格式文件
XmlTextWriter writer=new XmlTextWriter(Server.MapPath("e12_2_5.htm"),null);
Xslt.Transform(nav,null,writer);
writer.Close();//上句执行转换,nav查找文档节点,转换后由writer写入html文件
}
</script>
<body>
<form runat=server>
<asp:button text="转换XML文件为Html文件" Onclick="Btn_Click" runat=server/>
</form>
</body>
</html>
可用浏览器IE显示这个Html文件。转换后的Html文件,有时不能正确显示中文,可在标记<html>后加入如下语句,表示使用中文。
<head><meta http-equiv="Content-Type" content="text/html; charset=gb2312"></head>
12.3 对XML文档的处理
对XML文档的处理是指读取或查找XML文档中指定数据或标记、用程序生成XML文档,修改XML文档等。当前处理XML文档的方法主要有两种:DOM (Document Object Model)和SAX(Simple API for XML)。在.Net框架的System.XML命名空间为处理XML文档提供了若干类。.Net框架支持DOM,提供了XmlDocument类可以按节点读出或查找数据及元素,还可以增加节点,修改数据。.Net框架不支持SAX,但可以使用XmlTextReader类对XML数据流进行快速顺序只读访问,按节点读出或查找指定数据或标记。提供了XmlTextWriter类可用语句快速顺序生成XML文件。本节介绍这些方法。
12.3.1 使用XmlTextReader类读XML文件
XmlTextReader类可以读取XML文件,但只提供非缓存的只进、只读访问。这意味着使用XmlTextReader无法编辑属性值或元素内容,也无法插入和移除节点。
例子e12_3_1:本例用来读出e12_1_6.xml文件中每本书的书名、作者、出版日期、价格等数据。使用XmlTextReader类读XML文档各种元素只能顺序读出。运行效果如下图。
<%@ Import Namespace="System.Xml" %>
<html>
<script Language="C#" runat=server>
public void Page_Load(Object sender, EventArgs e)
{ XmlTextReader dr= new XmlTextReader(Server.MapPath("e12_1_6.xml"));
while(dr.Read())//顺序读出每一个节点
if(dr.NodeType==XmlNodeType.Text)//如果是文本节点,读出数据
ListBox1.Items.Add(dr.Value);//dr.Value是本节点的值
}
</script>
<body>
<form runat=server>
读XML文件数据<br>
<asp:ListBox id="ListBox1" runat="server"/>
</form>
</body>
</html>
在XML文档结构中,把XML文档的基本组成单元叫做节点,例如例子e12_1_4中的XML文档中,<学生>、<编号>、001、</编号>、<姓名>…都是节点。XmlTextReader类的方法Read()读Xml文档时,按节点在XML文档中的顺序逐一读出每一个节点。
XML文档的节点分为两大类,第一类是文本节点,即XML文档的数据。在两个标记之间的文本被称为一个文本节点,例如,<书名>SQL实用全书</书名>中的"SQL实用全书"是一个文本节点。文本节点的类型是XmlNodeType.Text,dr.Value为数据(dr意义见上例)。
第二类是非数据节点,它又可以分为以下几大类:注释节点、声明节点、开始标记节点,结束标记节点,统称为Xml文档的非数据节点。例如,<!--这是一个注释-->是注释节点,节点类型为:XmlNodeType.Comment,dr.Value为注释的内容,这里为"这是一个注释"。<?xml version="1.0" encoding="GB2312" ?>是声明节点,声明节点的节点类型为:XmlNodeType.XmlDeclartion。本声明节点包括两个声明:xml version="1.0"和encoding= "GB2312"。dr.Name为声明的名称,这里为xml version和encoding;dr.Value为声明的值,这里为"1.0"和"GB2312"。<book 出版社="电子工业出版社">是开始标记节点,其节点类型为:XmlNodeType.Element。dr.Name 为标记名称,这里为book。出版社被称为属性名字(Name),"电子工业出版社"被称为属性的值(Value),可以有多个属性,dr.AttributeCount 表示属性的个数,用方法dr.GetAttribute(i)得到第i个属性的值,如果要同时得到属性名字和属性的值,可以使用方法dr.MoveToFirstAttribute()移到第1个属性,方法dr.MoveToNextAttribute()移到下1个属性,然后用dr.Name得到属性名字,用dr.Value得到属性的值。</book>是结束标记,节点类型为:XmlNodeType.EndElement。dr.Name 得到标记名称,这里为book。
本网页的Page_Load方法中,用dr.Read()读Xml文档,每次读出一个节点的数据,用语句if(dr.NodeType==XmlNodeType.Text)判断是否是文本节点,如果是文本节点,则把文本内容加到ListBox1。
12.3.2 使用XmlTextReader类读XML文档标记
例子e12_3_2A:本例用来读出e12_1_6.xml文件book标记的属性。具体内容如下:
<%@ Import Namespace="System.Xml" %>
<html>
<script Language="C#" runat=server>
public void Page_Load(Object sender, EventArgs e)
{ XmlTextReader dr= new XmlTextReader(Server.MapPath("e12_1_6.xml"));
while(dr.Read())
if(dr.NodeType==XmlNodeType.Element)//判断是否为开始标记
for(int i=0;i<dr.AttributeCount;i++)
ListBox1.Items.Add(dr.GetAttribute(i));
}
</script>
<body>
<form runat=server>
读XML文件开始标记的属性<br>
<asp:ListBox id="ListBox1" runat="server"/>
</form>
</body>
</html>
例子e12_3_2B:如果显示e12_1_6.xml文档注释,修改上例Page_Load方法如下:
public void Page_Load(Object sender, EventArgs e)
{ XmlTextReader dr= new XmlTextReader(Server.MapPath("e12_1_6.xml"));
while(dr.Read())
if(dr.NodeType==XmlNodeType.Comment)
ListBox1.Items.Add(dr.Value);
}
例子e12_3_2C:如果显示e12_1_6.xml文档声明,修改上例Page_Load方法如下:
public void Page_Load(Object sender, EventArgs e)
{ XmlTextReader dr= new XmlTextReader(Server.MapPath("e12_1_6.xml"));
while(dr.Read())
if(dr.NodeType==XmlNodeType.XmlDeclaration)
ListBox1.Items.Add(dr.Name+" "+dr.Value);
}
12.3.3 使用XmlTextWriter类写XML文档
XmlTextWriter类提供了快速、非缓存、只进方法生成XML文档的方法,可以生成包含XML数据的流或文件。该类属性Formatting为Formatting.None,表示不使用特殊的格式设置XML文档,这是默认选项;如果为Formatting.Indented,表示使子元素根据Indentation和IndentChar设置缩进。
<%@ Import Namespace="System.Xml" %>
<html>
<script language="c#" runat=server>
void Btn_Click(Object src,EventArgs e)
{ string s="D:\\asp\\WriteBook\\e12_3_3.xml";
System.IO.FileStream myFileStream=//创建一个写入XML数据的文件流
new System.IO.FileStream(s,System.IO.FileMode.Create);
XmlTextWriter writer=//使用文件流对象创建一个XmlTextWriter对象
new XmlTextWriter(myFileStream, System.Text.Encoding.Unicode);
writer.Formatting = Formatting.Indented;
writer.WriteStartElement("学生");//写开始标记<学生>
writer.WriteAttributeString("编号", "001");//增加属性,标记为<学生 编号="001">
writer.WriteElementString("姓名", "张三");//写:<姓名>张三</姓名>
writer.WriteElementString("性别", "男");
writer.WriteElementString("年龄", "20");
writer.WriteEndElement();//写结束标记<学生/>
writer.Close();
}
</script>
<body>
<form runat=server>
<asp:button text="用程序写XML文件" Onclick="Btn_Click" runat=server/>
</form>
</body>
</html>
用程序写出的XML文档的书局格式如下:
<学生 编号="001">
<姓名>张三</姓名>
<性别>男</性别>
<年龄>20</年龄>
</学生>
12.3.4 文档对象模型(DOM)使用
文档对象模型(DOM)类是XML文档在内存中表示形式。DOM使程序员能够以编程方式读取、操作和修改XML文档。DOM的节点的概念和12.3.1节中叙述的概念完全相同,因此也可以使用类似XmlTextReader类的方法读出XML文档的数据和非数据节点。下边仅给出读出XML文档的数据的例子,读XML文档中的非数据节点请读者完成。
例子e12_3_4A:使用XML文档对象模型(DOM)读出e12_1_6.xml文件中每本书的书名、作者、出版日期、价格等数据。
<%@ Import Namespace="System.Xml" %>
<html>
<script Language="C#" runat=server>
public void Page_Load(Object sender, EventArgs e)
{ XmlDocument doc = new XmlDocument();//创建XmlDocument类的实例
doc.Load(Server.MapPath("e12_1_6.xml") );//读XML文件到内存,形成DOM结构
XmlNodeReader dr=new XmlNodeReader(doc);
while(dr.Read())
if(dr.NodeType==XmlNodeType.Text)
ListBox1.Items.Add(dr.Value);
}
</script>
<body>
<h2>使用Xml文档对象模型</h2>
<form runat=server>
<asp:ListBox id="ListBox1" runat="server"/>
</form>
</body>
</html>
例子e12_3_4B:用文档对象模型(DOM)创建一个XML文档。
<%@ Import Namespace="System.Xml" %>
<%@ Import Namespace="System.IO" %>
<html>
<script Language="C#" runat=server>
public void Page_Load(Object sender, EventArgs e)
{ XmlDocument doc = new XmlDocument();
doc.LoadXml("<book ISBN='1-861001-57-5'>" +
"<title>Pride</title>" + "</book>");
string s="D:\\asp\\WriteBook\\e12_3_4B.xml";
doc.Save(s);
}
</script>
<body>
<h2>运行此网页创建一个XML文档,请用IE察看</h2>
</body>
</html>
例子e12_3_4C:为例子e12_3_4B中创建的XML文件e12_3_4B.xml增加一个新节点。
<%@ Import Namespace="System.Xml" %>
<%@ Import Namespace="System.IO" %>
<html>
<script Language="C#" runat=server>
public void Page_Load(Object sender, EventArgs e)
{ XmlDocument doc = new XmlDocument();
doc.Load(Server.MapPath("e12_3_4B.xml"));
XmlNode root = doc.DocumentElement;//获得根节点,既book节点
XmlElement elem = doc.CreateElement("price");//建立新节点,节点名称为price
elem.InnerText="19.95";//新节点的值为19.95
root.AppendChild(elem);//为根节点(book节点)增加一个子节点
doc.Save(Server.MapPath("e12_3_4C.xml"));//存XML文件
}
</script>
<body>
<h2>运行此网页增加一个新节点,请用IE察看</h2>
</body>
</html>
也可以用方法InsertAfter在参数2指定的节点后插入参数1指定的节点,用方法InsertBefore在参数2指定的节点前插入参数1指定的节点,方法的参数1是要插入的节点,例如上例的elem, 参数2指定1个节点,是插入的参考位置,例如:root.FirstChild。
例子e12_3_4D:为例子e12_3_4B中创建的XML文件e12_3_4B.xml增加一个新属性。
<%@ Import Namespace="System.Xml" %>
<%@ Import Namespace="System.IO" %>
<html>
<script Language="C#" runat=server>
public void Page_Load(Object sender, EventArgs e)
{ XmlDocument doc = new XmlDocument();
doc.Load(Server.MapPath("e12_3_4B.xml"));
XmlAttribute newAttr=doc.CreateAttribute("genre");//创建新属性,Name="genre"
newAttr.Value = "novel";//属性值为"novel"
XmlAttributeCollection attrColl=doc.DocumentElement.Attributes;//得到根节点属性
attrColl.Append(newAttr);//为根节点增加一个新属性
doc.Save(Server.MapPath("e12_3_4D.xml"));
}
</script>
<body>
<h2>运行此网页增加一个属性,请用IE察看</h2>
</body>
</html>
例子中的语句doc.DocumentElement为XML文档的根节点,本例为book节点。doc.DocumentElement.Attributes语句得到根节点的所有属性。任何XML文档节点都可用XmlNode节点类对象来代表,XmlNode类属性Attributes表示开始标记节点中的所有属性,该属性是XmlAttributeCollection类对象,可以象操作一个普通数组那样修改该节点的属性。例中用Append方法为根节点增加了一个属性。attrColl.InsertAfter(newAttr, attrColl.ItemOf(0))则表示在第1个属性之后增加新属性,attrColl.InsertBefore(newAttr, attrColl[0]) 则表示在第1个属性之前增加新属性。其它方法,例如Remove、RemoveAll等方法意义可以察看XmlAttributeCollection类帮助文档。请注意对这个数组的操作,就是对节点属性的操作。
例子e12_3_4E:查找e12_1_6.xml文档指定节点,修改该节点数据。网页文件如下,第1条语句中的Debug="true"表示允许调试,当发现错误时,在IE浏览器中显示错误信息。
<%@ Page Language="C#" Debug="true" %>
<%@ Import Namespace="System.Xml" %>
<%@ Import Namespace="System.IO" %>
<html>
<script Language="C#" runat=server>
public void Page_Load(Object sender, EventArgs e)
{ XmlDocument doc = new XmlDocument();
doc.Load(Server.MapPath("e12_1_6.xml"));//e12_1_6.xml和本网页在同一目录
XmlNode book;
XmlNode root = doc.DocumentElement;//得到根节点
//下句查找满足条件的标记名为book的第1个节点,条件为其子节点书名="SQL实用全书"
book=root.SelectSingleNode("descendant::book[书名='SQL实用全书']");
//book.LastChild.InnerText="19.00";//修改最后一个子节点文本,此句也正确
book["价格"].InnerText="19.00";//修改价格节点数据
doc.Save(Server.MapPath("e12_3_4E.xml"));
}
</script>
<body>
<h2>运行此网页修改指定节点数据,请用IE察看</h2>
</body>
</html>
例子e12_3_4F:本例显示XML文件e12_1_6.xml的所有书名。
<%@ Page Language="C#" Debug="true" %>
<%@ Import Namespace="System.Xml" %>
<%@ Import Namespace="System.IO" %>
<html>
<script Language="C#" runat=server>
public void Page_Load(Object sender, EventArgs e)
{ XmlDocument doc = new XmlDocument();
doc.Load(Server.MapPath("e12_1_6.xml"));
//下句得到XML文档中"书名"节点放到节点数组中
XmlNodeList elemList = doc.GetElementsByTagName("书名");
for(int i=0; i < elemList.Count; i++)
{ ListBox1.Items.Add(elemList[i].InnerXml);//显示书名节点的内部数据即书名
}
}
</script>
<body>
<form runat=server>
<asp:ListBox id="ListBox1" runat="server"/>
</form>
</body>
</html>
使用Xml文档对象模型要把整个XML文件到内存,形成DOM结构,但可以修改XML文档。例子e12_2_5也是使用Xml文档对象模型的例子。
12.3.5 用XML Schema验证XML架构
一个XML文档首先应当是格式良好的,为了验证格式的正确性,可以使用XML Schema (XML架构,XSD)对一个XML文档进行验证,下例介绍验证的具体方法。
例子e12_3_5:首先按12.4.3节方法为文件e12_1_4.xml建立XSD文件e12_1_4.xsd,然后用下边的网页文件用e12_1_4.xsd对e12_1_4.xml的架构进行验证。
<%@ import namespace="System.Xml" %>
<%@ import namespace="System.IO" %>
<%@ import namespace="System.Xml.Schema" %>
<html>
<script language="c#" runat=server>
void Page_Load(object sender, System.EventArgs e)
{ FileStream fs=new FileStream(Server.MapPath("e12_1_4.xml"), FileMode.Open);
//创建XmlValidatingReader类的对象
XmlValidatingReader vr=new XmlValidatingReader(fs,XmlNodeType.Element,null);
vr.Schemas.Add(null, Server.MapPath("e12_1_4.xsd"));//加载XML架构文档
vr.ValidationType = ValidationType.Schema;//说明是根据XML架构验证
vr.ValidationEventHandler +=new ValidationEventHandler(ValidationHandler);
while(vr.Read());//对文档进行验证,验证不通过,产生事件验证失败事件
label1.Text="架构正确";
fs.Close();
}
private void ValidationHandler(object sender,ValidationEventArgs args)//验证失败事件函数
{ label1.Text="架构不正确";//显示验证失败的消息
}
</script>
<body>
<form runat=server>
<asp:Label id="label1" runat=server/>
</form>
</body>
</html>
12.4 数据库和XML
XML提供了异构数据库之间交换数据的一种方法。本节介绍这种方法。
12.4.1 数据库数据存为XML文档
察看e12_1_6.xml文件和数据库的表的对应关系,标记<bookstore>之间的内容可以看作一个数据库的表,标记<book>之间的内容可以看作一个数据库的表的一个记录,标记<书名>、<作者>、<出版日期>、<价格>可以看作一个数据库的表的字段,这些标记之间的文本可以看作这些字段的数据。因此,可以用XML文档来表示一个数据库表。由于XML文档可以被任何一种计算机系统所解释,因此XML提供了异构数据库之间交换数据的一种方法。
数据库表的字段还有一些其它属性,例如,字段的数据类型,为了表示这些属性及其数据库表结构,可以使用DTD(Document Type Define,文档定义类型)或XML Schema来描述XML文档的数据结构和组成结构的元素类型。微软的.Net系统支持用XML Schema来描述XML文档的数据结构。VS.Net提供了将数据库表存为带XML架构和不带XML架构XML文件的方法,下边例子介绍实现的具体步骤。
例子e12_4_1:将数据库studentI.mdb中的studnt表存为带XML架构或不带XML架构的XML文件。使用VS.Net建立这个ASP.Net网页的具体步骤如下:
(1) 创建Web应用程序项目,项目名为e12_4_1。
(2) 按照8.12节的例子e8_12中的第(4)步到第(8)步创建OleDbConnection对象、OleDbDataAdapter对象和数据集DataSet对象。
(3) 在窗体中放置控件DataGrid,其属性Name=dataGrid1,属性DataSource为dataSet11,属性DataMember为Student。
(4) 在Page_Load函数中增加语句如下:
oleDbDataAdapter1.Fill(dataSet11);
DataGrid1.DataBind();
(5) 运行,应能在DataGrid控件中看到数据库studentI.mdb中的studnt表的数据。
(6) 增加一个按钮,属性ID为Button1,属性Text为"将数据库表存为带XML架构XML文件",为其增加事件处理函数如下:
private void Button1_Click(object sender, System.EventArgs e)
{ string s="D:\\asp\\e12_4_1\\e12_4_1A.xml";
dataSet11.WriteXml(s,XmlWriteMode.WriteSchema);
}
(7) 增加一个按钮,属性ID为Button2,属性Text为"将数据库表存为不带XML架构XML文件",为其增加事件处理函数如下:
private void Button1_Click(object sender, System.EventArgs e)
{ string s="D:\\asp\\e12_4_1\\e12_4_1B.xml";
dataSet11.WriteXml(s,XmlWriteMode.IgnoreSchema);
}
(8) 运行,单击两个按钮,可以创建带XML架构和不带XML架构XML文件,文件名为"e12_4_1A.xml"和"e12_4_1B.xml"。用浏览器察看这两个XML文件,可以看到它们的区别。不使用VS.Net,使用记事本建立这个ASP.Net网页不能完成此功能。
12.4.2 读XML文档到DataSet对象
例子e12_4_2:把12.4.1节生成的带XML架构或不带XML架构XML文件用控件DataGrid显示。用记事本生成的网页文件如下:
<%@ Import Namespace="System.Xml" %>
<%@ Import Namespace="System.Data" %>
<html>
<script Language="C#" runat=server>
public void Page_Load(Object sender, EventArgs e)
{ string FileNameString=Server.MapPath("e12_4_1B.xml");//或e12_4_1A.xml
DataSet ds = new DataSet();
ds.ReadXml(FileNameString);
DataGrid1.DataSource=ds.Tables[0].DefaultView;
DataGrid1.DataBind();
}
</script>
<body>
<h2>读带XML架构和不带XML架构XML文件</h2>
<form runat=server>
<asp:DataGrid id="DataGrid1" runat="server"/>
</form>
</body>
</html>
12.4.3 为自建的XML文档建立XSD文件
自己创建的XML文档,如果用手工创建XML Schema是比较困难的,下例介绍如何使用VS.Net为一个XML文档创建XML Schema。
例子e12_4_3:为自建的XML文档e12_1_4.xml建立XSD文件。
(1) 运行VS.Net,打开e12_1_4.xml文件。
(2) 单击VS.Net菜单"XML"|"创建架构"菜单项,将创建e12_1_4.xsd文件,打开此文件,可以修改每个字段的数据类型。然后存盘。
(3) e12_1_4.xsd也是一个XML文档,请用IE浏览器打开e12_1_4.xsd,察看e12_1_4.xsd的内容。
习题
(1) 创建一个记录某专业学生学习科目的XML文档。
(2) 读出e12_1_6.xml文件中所有开始和结束标记,用ListBox控件显示。
(3) 用Xml文档对象模型(DOM)读e12_1_6.xml文件开始和结束标记,用ListBox控件显示。
(4) 编写一个程序,读e12_1_4.xml文件,在网页中按照如下格式输出:
<学生>
<编号>001</编号>
<姓名>张三</姓名>
<性别>男</性别>
<年龄>20</年龄>
</学生>
(5) 编写一个程序,读e12_1_6.xml文件到DataSet中,用TextBox控件显示每一个记录。(提示:将XML文件读入DataSet后,就可以将此文件的内容看作一个数据库表。)
(6) 例子e12_3_1中如果希望只显示书名,如何实现?(提示:先找开始标记为”书名”的节点,如果找到,读下一节点,用dr.Value得到书名。)
(7) 修改例子e12_3_2A,使其能够读出节点属性的名称和和属性的值。
(8) 修改例子e12_3_4E,查找例子e12_1_6.xml中作者名字为张三的书的价格为20.00元。
(9) 对e12_1_6.xml的架构进行验证。
(10) 用记事本程序编制的网页文件,可以将SQL2000数据库系统的例子数据库NorthWind的表Employees存为带XML架构和不带XML架构XML文件。请写出网页文件。
(11) 在8.13节,用xsd文件记录了两个表的主从关系,如果要把两个表以及两个表的主从关系转换为XML文档,则xsd文件要单独存为一个文件,可以使用方法WriteXmlSchema()将DataSet中架构写入XML文档。请把8.13节例子中数据库数据用XML文档保存。