- 你所在位置:首页 〉VS.net〉XML〉XML经验〉XSLT 和 ASP.NET 实用性对比 (Extreme XML)
- XSLT 和 ASP.NET 实用性对比 (Extreme XML)
- 作者:佚名 文章来源:http://msdn2.microsoft.com/zh-cn/default.aspx 发布日期:2007-07-07 浏览次数:520
-
- 打印这篇文章
-
下载 Xml02192001.exe。
简介
人们正在使用 XML 来管理大型复杂网站的发布过程。示例包括 interview with Mike Moore(介绍 www.microsoft.com 如何使用 XML 来管理其复杂的需要)和 Streamlining Your Web Site Using XML(深入概述如 Dell 这样的公司如何使用 XML 来简化它们的整个发布过程)。
许多客户会问我这些问题:他们是否应当放弃 XML/XSL 而转向编写大量 C# ASP.NET 代码?如果他们已经在 XSLT 方面进行了大量投资,ASP.NET 是否为它们提供任何价值?是否存在一种折衷方案使他们能够同时获得 XSLT 和 ASP.NET 的最佳功能?如果存在,每种技术有哪些优缺点,人们应当在什么情况下使用它们?
我将深入剖析一个具体示例,以便可以对 XSLT 和 ASP.NET 进行比较和对照,这个示例就是 MSDN TOC。MSDN 发现 XML 最适于管理它的大型目录 (TOC)。此 TOC 的内容来自公司内的数百个组。XML 格式提供了一种将不同的后端过程组合在一起的方法,这些过程一直很难改变。XML/XSL 还使得到达不同平台上的不同浏览器成为可能。
假设开发人员发现 XML 最适于管理进入网站的后端数据,让我们看一下如何提取此 XML 数据以及如何将它转换为 HTML。首先,我将看一下 ASP.NET 解决方案。
ASP.NET 解决方案
我的朋友 Dan Wahlin 为 ASP.NET 编写了一个基于 XML 的菜单控件 — Make Web Browsing Easier,它是我所见到的最佳控件之一。(您需要具备 DevX 成员资格才能访问本文。)在经过 Dan 的允许之后,我已经将此代码进行了一些增强并将它打包在 WebControl 中。
图 1. 打包在 WebControl 中的基于 XML 的菜单
WebControl 随后以可重用的方式嵌入到 ASP.NET 页中:
〈XmlMenu:XmlMenuBar id="MyMenus" runat="Server" src="menus.xml">
〈/XmlMenu:XmlMenuBar>
现在,让我们看一下此控件的实现。呈现 HTML 菜单的逻辑首先从重写 WebControl Render() 虚拟方法开始。
protected override void Render(HtmlTextWriter output) {
XmlDocument doc = (XmlDocument)Context.Cache["Master"];
if (doc == null)
{
string filepath = Context.Server.MapPath(_src);
doc = new XmlDocument();
doc.Load(filepath);
ResolveSubMenus(doc);
Context.Cache.Insert("Master", doc,
new CacheDependency(filepath));
}
RenderMenuBar(doc, output);
}
此处的通用算法是:加载 menu.xml 文件,解析此文件中的任何外部引用,然后将主菜单文档缓存到内存中,以便提高随后的请求速度。这种缓存功能是 ASP.NET 的一个重要功能。
解析外部子菜单是通过以下方法来完成的:使用 XPath 遍历文档,查找 src 属性,加载子菜单文档,然后将它们合并到主文档中:
void ResolveSubMenus(XmlDocument doc)
{
DocumentNavigator nav = new DocumentNavigator(doc);
nav.Select("//*[@src]"); // XPath for finding all src attributes.
while (nav.MoveToNextSelected())
{
string src = nav.GetAttribute("src");
XmlDocument submenudoc = new XmlDocument();
submenudoc.Load(Context.Server.MapPath(src));
DocumentNavigator nav2 = new DocumentNavigator(submenudoc);
nav2.MoveToDocumentElement();
nav.MoveChildren(TreePosition.LastChild, nav2);
}
}
注:预编译 XPath 表达式的功能是 .NET 框架中新 XML 类的一个重要功能,这为我们带来了更好的性能。
接着,我们采用这个完全解析的主 XmlDocument,并将 HTML 输出结果写入该 ASP.NET 框架原来传递到的HtmlTextWriter 中。顶层菜单栏通过使用预定义的级联式样式表 (CSS) 类以 HTML 表的形式写出,以便菜单的总体外观可通过一个单独的 .css 文件控制。
这需要两个步骤:写出顶层菜单栏项,然后写出菜单本身。这些菜单是用隐藏的 DIV 元素写出的,一些客户端 ECMAScript 代码会以动态方式弹出这些元素。
object _linkexpr; // pre-compiled XPath expressions.
object _nameexpr;
void RenderMenuBar(XmlDocument doc, HtmlTextWriter output)
{
output.Write("〈table width='100%' class=clsMenuBar border='0' ");
output.Write("cellspacing='0' cellpadding='0'>\n〈tr>〈td>\n");
output.Write("〈table class=clsMenuBar border=0 cellspacing=0 ");
output.Write("cellpadding='0'>\n");
output.Write("〈tr align='left' valign='middle'>\n〈td> 〈/td>\n");
DocumentNavigator nav = new DocumentNavigator(doc);
_linkexpr = nav.Compile("link");
_nameexpr = nav.Compile("name");
nav.MoveToDocumentElement();
RenderMenuBarItems(nav, output);
output.Write("〈/tr>〈/table>\n〈/td>〈/tr>〈/table>\n");
nav.MoveToDocumentElement();
RenderMenus(nav, output);
}
通过遍历根元素的子级,为菜单栏生成表单元格和特殊的可自定义菜单栏空格单元格,来呈现菜单栏项。它还生成要与鼠标进行交互的 DHTML,以便在鼠标移到菜单上时会弹出子菜单。使用各种 XPath 表达式,可以弹出根层菜单项中的每个菜单的名称和链接部分。正如您所看到的一样,此代码相当长。为了将字符串串联减少到最少,输出调用是以一种相当冗长的方式来完成的。这将使性能大约提高 10%。
void RenderMenuBarItems(XmlNavigator nav, HtmlTextWriter output)
{
int i = 0;
nav.Select("child::*"); // select child elements
bool first = true;
if (!nav.MoveToFirstChild()) return;
do
{
if (nav.Name == "menu")
{
if (! first) {
output.WriteLine("〈td class='clsMenuBarCell' nowrap>");
output.Write(_barSpacer);
output.Write("〈/td>");
}
string name = "menu" + i++;
output.Write("〈td class='clsMenuBarCell' nowrap>");
output.Write("〈a id='start' class='clsMenuBarCell' ");
output.Write("style='text-decoration: none; cursor: hand'");
string target = "";
string href = "";
if (nav.SelectSingle(_linkexpr)) // is it also a link ?
{
target = nav.GetAttribute("target");
href = nav.InnerText;
nav.MoveToParent();
}
if (HasSubMenu(nav)) // submenu ?
{
output.Write("onmouseover='startIt(\"");
output.Write(name);
output.Write("\",this,0)' ");
}
else if (href != "")
{
output.Write(" onMouseOver=\"stateChange('',this,'');hideDiv(0);\" ");
output.Write("onMouseOut=\"stateChange('',this,'');\" ");
}
if (href != "")
{
output.Write("href='");
output.Write(href);
output.Write("' onClick=\"Select('");
output.Write(href);
output.Write("','");
output.Write(target);
output.Write("')\" ");
}
output.Write(">");
nav.SelectSingle(_nameexpr);
string text = nav.InnerText;
output.Write(text);// grab the menu name
nav.MoveToParent();
first = false;
}
}
while (nav.MoveToNext());
}
接着,将菜单呈现在隐藏的 DIV 元素中。赋予每个菜单一个唯一的 ID,客户端 ECMAScript 代码使用这些 ID 将菜单项以及与之相关的子菜单联系在一起:
void RenderMenus(XmlNavigator nav, HtmlTextWriter output)
{
int i = 0;
if (!nav.MoveToFirstChild()) return;
do
{
if (nav.Name == "menu")
{
string name = "menu" + i++;
