大家說我說的跟.net一點關系都沒有,可是第一篇我也說了啊,僅僅是一個HELLO的例子啊。現在哪個人不忙啊。只能一點一點的寫了。。
好了,廢話少說,我們先看看實際的情況,然后根據情況來寫一個小小的例子吧,這次有Demo下載嘍。。。
現實項目中的情況:
現實的項目中,我們不可能是有著一堆的XML的,這些XML有可能一部分是來自于現有的文檔,有一部分可能是從其它服務提供者那里得到的,當然也有可能是自己寫的服務提供的……
總之一句話,不到真正開始了,天曉得xml是哪里來的。
現實很殘酷,我們也很聰明,辦法總是有的,而且非常簡單,這里我建立了一個Index.xml,一個Index.xslt文件放到網站的Album目錄中:
可以通過一個配置文檔來配置所有XML的URL,從而增進可維護性。
Index.xml文件的內容:
<?xml version="1.0" encoding="utf-8" ?>
<?xml-stylesheet type="text/xsl" href="Index.xslt"?>
<services>
<album>XML FILE URL</album>
</services>
Index.xslt文件的內容:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="html" indent="yes" doctype-public="-//W3C//DTD XHTML 1.1//EN" doctype-system="http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"/>
<xsl:template match="services">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Index</title>
</head>
<body>
<h1>
<xsl:value-of select="album"/>
</h1>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
當確認上述兩敗個文檔都正確建立后,再建立一個Index.ashx文件,用于提供XML數據,并且將Index.xml文件的album元素的值改為Index.ashx的URL。
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/xml";
context.Response.ContentEncoding = Encoding.UTF8;
List<Album> albums = new AlbumBusiness().Select("ArtistId=1");
XmlSerializer albumXmlSerializer = new XmlSerializer(albums.GetType(), new XmlRootAttribute("Albums"));
albumXmlSerializer.Serialize(context.Response.OutputStream, albums);
}
準備工作都做好了,看看輸出的XML大致的樣式吧:
<?xml version="1.0"?>
<Albums xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Album>
<AlbumId>386</AlbumId>
<GenreId>1</GenreId>
<ArtistId>1</ArtistId>
<Title>For Those About To Rock We Salute You</Title>
<Price>8.99</Price>
<AlbumArtUrl>/Content/Images/placeholder.gif</AlbumArtUrl>
</Album>
<Album>
<AlbumId>387</AlbumId>
<GenreId>1</GenreId>
<ArtistId>1</ArtistId>
<Title>Let There Be Rock</Title>
<Price>8.99</Price>
<AlbumArtUrl>/Content/Images/placeholder.gif</AlbumArtUrl>
</Album>
</Albums>
XSL通過variable標記聲明變量。
這里要說到xsl和變量和函數了,將下面的內容添加到Index.xslt文檔的template下面:
<xsl:variable name="AlbumServiceUrl" select="album"></xsl:variable>
<xsl:variable name="Albums" select="document($AlbumServiceUrl)/Albums/Album"></xsl:variable>
xsl:variable 標記用于聲明變量。其name屬性用于指定此變量的名稱,select屬性用于選擇變量的內容,在這里不難看出,它是選擇了services節點下的album節點的值。這里使用的是相對path。其文檔來源由所在的template來決定。
Albums變量的select中使用了document函數,此函數使用一個文檔路徑的參數來載入一個xml文檔。其后面跟著的“/Albums/Album” XPpath指定了選擇我們指定的那個文檔下面所有的Album節點存放在此變量中。
在XSL中,引用變量的方式是在變量名前面加一個$符號。
如上面的$AlbumServiceUrl.
接下來要輸出些東西了,我們還要再看一個新的XSL標記:xsl:for-each:
<ul>
<xsl:for-each select="$Albums">
<li>
<xsl:value-of select="Title"/>
</li>
</xsl:for-each>
</ul>
前面已經說過,我們是把所有Album節點都存到了Albums變量中了,那么這里也應當很容易看得出我們是在遍歷所有Album節點,然后把其子元素Title的值以li的形式輸出。再加上最外面包圍的一個ul,整個一個無序列表就出來了。
這里無需編譯,可以直接運行Index.xml以查看結果,我們可以把這些內容直接返還給客戶端。樣式表(xslt)文件因為是靜態的,所以客戶端只需要下載一次既可,可以暫時認為和css文件類似,只是其功能側重點不同而已。
我想,看到這里,你一定會想到,如果做分布式的話,xslt確實可以給出不錯的解決方案。
問題像是已經都解決了。但由于返回的只是xml和xslt文檔,加上搜索引擎對xml和xslt支持并不怎么樣,還可能有一些瀏覽器根本不支持xslt的情況,那么就要用到服務器端的編譯了:
再建立一個Transform.ashx文件:
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/html";
context.Response.ContentEncoding = Encoding.UTF8;
string xmlUrl = context.Server.MapPath(@"Index.xml");
string xsltUrl = context.Server.MapPath(@"Index.xslt");
XslCompiledTransform trans = new XslCompiledTransform();
trans.Load(xsltUrl, new XsltSettings() { EnableDocumentFunction = true, EnableScript = true }, new XmlUrlResolver());
trans.Transform(xmlUrl, null, context.Response.OutputStream);
}
還有一個問題就是怎么知道是蜘蛛訪問的還是用戶訪問的呢?目前我是使用的檢查UserAgent里面的值。之前在XSLT 入門--實際應用中使用的是排除蜘蛛的方式,這里要使用排除已知支持XSLT的瀏覽器的方式:
在web.config中appSettings節點下加入如下值:
<add key="XsltSupportBrowsers" value="Chrome|MSIE 9.0"/>
然后將Transform.ashx中ProcessRequest方法更改如下:
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/html";
context.Response.ContentEncoding = Encoding.UTF8;
string[] xsltSupportBrowsers = ConfigurationManager.AppSettings["XsltSupportBrowsers"].Split('|');
foreach (var xsltSupportBrowser in xsltSupportBrowsers)
{
if (context.Request.UserAgent.Contains(xsltSupportBrowser))
{
context.Response.ContentType = "text/xml";
context.Response.WriteFile(context.Server.MapPath("Index.xml"));
return;
}
}
string xmlUrl = context.Server.MapPath(@"Index.xml");
string xsltUrl = context.Server.MapPath(@"Index.xslt");
XslCompiledTransform trans = new XslCompiledTransform();
trans.Load(xsltUrl, new XsltSettings() { EnableDocumentFunction = true, EnableScript = true }, new XmlUrlResolver());
trans.Transform(xmlUrl, null, context.Response.OutputStream);
}
Index.xml文件和Index.xslt文件的路徑是固定的,不會改變的,可以直接寫。
通過httpHandlers配置節點將請求映射到處理程序。
那么我們也可以把所有請求都交給一個Handler來處理,xslt和xml文件我們都放到同一個目錄的情況下可以這樣做:
在httpHandlers配置節點中加入一條配置,將所有對.xhtml文件的請求都交給我們的Transform Handler來處理:
<add verb="*" path="*.xhtml" type="MusicStore.Web.Albums.Transform"/>
.ashx文檔(一般處理程序)可大大簡化xslt的實施。
然后將Transform.ashx中ProcessRequest方法也可以再次更改成通用的方式,在這里沒有做異常處理,因為僅僅是為了說明能這樣做:
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/html";
context.Response.ContentEncoding = Encoding.UTF8;
string requestPhysicalPath = context.Request.PhysicalPath;
string requestFileName = requestPhysicalPath.Substring(0, requestPhysicalPath.LastIndexOf("."));
string xmlUrl = requestFileName + ".xml";
string xsltUrl = requestFileName + ".xslt";
string[] xsltSupportBrowsers = ConfigurationManager.AppSettings["XsltSupportBrowsers"].Split('|');
foreach (var xsltSupportBrowser in xsltSupportBrowsers)
{
if (context.Request.UserAgent.Contains(xsltSupportBrowser))
{
context.Response.ContentType = "text/xml";
context.Response.WriteFile(xmlUrl);
return;
}
}
XslCompiledTransform trans = new XslCompiledTransform();
trans.Load(xsltUrl, new XsltSettings() { EnableDocumentFunction = true, EnableScript = true }, new XmlUrlResolver());
trans.Transform(xmlUrl, null, context.Response.OutputStream);
}
都搞定了,不過還有個問題還是現在說一下吧,那就是不是所有時候我們都愿意把所有的內容都寫到一個xslt中。例如:網站的頁頭和頁腳難道每個XSLT文件中都復制粘貼一份?這里再建立一個Albums.xslt來解決這個問題:
可以通過指定xslt模板的name屬性來聲明一個可訪問的xslt模板。
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="html" indent="yes"/>
<xsl:template name="Albums" match="album">
<xsl:variable name="AlbumServiceUrl" select="album"></xsl:variable>
<xsl:variable name="Albums" select="document($AlbumServiceUrl)/Albums/Album"></xsl:variable>
<ul>
<xsl:for-each select="$Albums">
<li>
<xsl:value-of select="Title"/>
</li>
</xsl:for-each>
</ul>
</xsl:template>
</xsl:stylesheet>
這里最主要的是使用了template的name屬性,也就是給模板起個名字。可以先認為就像.net里的服務器控件,給它起個ID就可以訪問。但性質是完全不同的。
call-template標記通過name屬性指定模板名稱來調用xslt模板。
調用xslt模板的方式也有多種:
這里使用<xsl:call-template />標記:
<xsl:call-template name="Albums" />
由于我們將此模板放到了Albums.xslt文件中,而不是在當前文檔中,所以得使用另一個標記來引入外部的xslt文件,
Import標記通過href屬性指定xslt URL來引入外部XSLT文件。
將此標記放到Index.xslt文檔的output標記之前:
<xsl:import href="Albums.xslt"/>
此時再將Index.xslt中的變量聲明與ul下的所有內容注釋或刪除。仍然可以得到想要的結果。
下載此項目源代碼
本文轉載自 http://www.cnblogs.com/javennie/archive/2011/09/28/xsltinaspnet.html