• ASP.NET 也可以做得很好 2.XSLT初识 在ASP.NET中使用XSLT


    ASP.NET 也可以做得很好 2.XSLT初识 在ASP.NET中使用XSLT

    大家说我说的跟.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下的所有内容注释或删除。仍然可以得到想要的结果。

    下载此项目源代码

    针对XML太大的问题,我稍修改了下.tt文档,把实体类的属性按xml属性的方式生成了。也稍改了下xslt文档。

    MusicStore_xslt_xml_modified_tt_template.rar

  • 相关阅读:
    webpack脚手架增加版本号
    background-image:url为空引发的两次请求问题
    vue中引入.svg图标,使用iconfont图标库
    mysql数据库
    vue 博客知识点汇总
    vue中显示markdown文件为html
    canvans知识点
    js如何实现一定时间后去执行一个函数
    CSS3选择器使用小结
    为什么margin-top不是作用于父元素
  • 原文地址:https://www.cnblogs.com/javennie/p/xsltinaspnet.html
Copyright © 2020-2023  润新知