|
![]() |
名片设计 CorelDRAW Illustrator AuotoCAD Painter 其他软件 Photoshop Fireworks Flash |
|
在我的新书《Java and XSLT》中介绍了Java与XSLT的技术组合。这篇文章从书中选出了我认为异常重要的10条技巧。但实际上这有限的10条只是粗略的描述了什么是可能的。其中大多数都集中在Java与XSLT的组合上,而不是在XSLT(可扩展样式表转变)技术规范。而更具体的信息,在文章结尾处指出了一些有价值的资源。 基本的XSL转变是异常简朴的:一个或多个包含着指令的XSLT样式表,这些指令定义了如何把XML数据转变成其他格式。XSLT处理器完成实际的工作;Sun微系统的Java API for XML Processing (JAXP)为不同种类的处理器提供了一套标准的Java接口。这里有一个用JAXP的API执行XSL转变的简朴例子: import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.stream.StreamSource; import javax.xml.transform.stream.StreamResult; import java.io.*; public class Transform { /** * Performs an XSLT transformation, sending the results * to System.out. */ public static void main(String[] args) throws Exception { if (args.length != 2) { System.err.println("Usage: java Transform [xmlfile] [xsltfile]"); System.exit(1); } File xmlFile = new File(args[0]); File xsltFile = new File(args[1]); // JAXP reads data using the Source interface Source xmlSource = new StreamSource(xmlFile); Source xsltSource = new StreamSource(xsltFile); // the factory pattern supports different XSLT processors TransformerFactory transFact = TransformerFactory.newInstance(); Transformer trans = transFact.newTransformer(xsltSource); trans.transform(xmlSource, new StreamResult(System.out)); } } 你可以点击这里下载一个很小的ZIP文件,这个文件包含了这个例子以及相应的XSLT样式表和XML数据文件。其中的README文件解释了怎样编译和运行这个例子。 这个这个例子是利用StreamSource从文件中读取数据,JAXP还能从SAX解释器或DOM树来读取XML数据。下面依次介绍我推荐的10条技巧: 1、 尽可能使用缓存。 执行XSLT转变是很耗费CPU和内存的,所以在任何时候进行可能的优化都是很有意义的。使用由XSLT驱动的Web应用,提高它的实时运行性能表现的最好方式之一就是使用各种类型的缓存技术。 图一举例说明了一个典型的使用XSL对数据库进行转变的Web应用。 图一、典型的XSL转变 与动态生成的XML不同,XSLT样式表是静态存储在文件中的。由于这些文件很少被改动,就可以用JAXP的javax.xml.transform.Templates接口把它们解析好了进内存里缓存起来。下面这个程序片断解释了这个过程是怎样完成的: Source xsltSource = new StreamSource(xsltFile); TransformerFactory transFact = TransformerFactory.newInstance(); Templates cachedXSLT = transFact.newTemplates(xsltSource); Transformer trans = cachedXSLT.newTransformer(); 当XSLT样式表通过Templates接口缓存进了内存中,现在它就可以被重复用于很多不同的转变里。最重要的好处是,这样就避免了重复把XSLT解析进内存。它也给了XSLT处理器一个机会来优化转变指令,就象编译器优化软件那样。 有人可能会想是否可以把XML数据也缓存进内存中。对于那些高度动态和个体化的应用,XML数据是随着每一个客户哀求而动态生成的并随时都在变化。对于这种应用,缓存是不实际的。对于很多其它类型的应用,XML数据可能改变的不是很频繁。当数据改变不是很频繁时,相对于缓存XML数据,将转变后的结果缓存可能更有意义。这是一种最快的可行性解决方案,推荐在任何可行的情况下使用。 2、部署前做测试。 在开发Web应用项目选择XML和XSLT的要害在于可以清晰的把数据、程序逻辑和表达分开。Java代码与后台数据源交互并生成XML数据,XSLT样式表把XML数据转变为XHTML(或WML,或其它),然后浏览器显示结果。 这种结构的一个独特的,然而时常被忽略的好处是它支持自动单元测试的能力。象JUnit这样的工具鼓励程序员去写适合自动单元测试的套件。这些测试大大的减少了在系统中加入新特性时所产生的错误。考虑一下一个典型的Java+XML+XSLT网站的这些组件: 用Java实现商业逻辑。由于Java代码没有和表达逻辑混在一起,就可以象其他任何Java代码相同测试它。 把应用数据转变为XML。这一步是特殊轻易的。只要生成XML然后用一个DTD或一个XML Schema验证它就行了。 把XML转变为XHTML。同样的,生成的XHTML可以用一个XHTML DTD来验证。虽然这样做不能证实信息是准确的,但是的确能保证XHTML被准确的生成而且是有效的。 与很多其它的Web开发技术不同,测试这些单元中的任何一个都不用将其部署到Web服务器上。这使自动单元测试更轻易被实现,自动单元测试也是极限编程(XP)技术的一个要害组成部分。 3、尽量让XSLT样式表简朴。 至少有两个理由要保持XSLT样式表简朴。第一,XSLT并不是一个象Java那样丰富的编程语言。虽然XSLT擅于转变,但是在样式表中嵌入太多的应用逻辑会使它变得相称复杂。因为这个原因,就应该在创建XML之前用Java实现尽可能多的商业逻辑。然后再用XSLT转变XML就应该简朴得多了。 第二个保持样式表简朴的理由是XSLT的语法不轻易被读懂。XML标签使XSLT很轻易解析和方便的做程序化的处理,但所有的那些XML标签也使得样式表不轻易被读懂。有几个小技巧可以帮助程序员更轻易读懂和处理XSLT样式表: 使用具有语法区分功能的编辑器,如Altova的XML Spy。 为每一个XSLT模板添加有区别的注释。这样就有助于打破那种在大堆被括在\\\'<\\\'和\\\'>\\\'的字符串里搜索时的单调。 对最高层的变量和样式表参数采用一定的命名规则。 把通用的方式取出来放进第二个样式表中,用<xsl:import>来重用代码。 4、和XSLT一起使用CSS。 这条技巧是和上一条联系在一起的,它也可以大大的减少XSLT样式表的复杂程度。 XSLT和CSS分别执行不同的任务并相互补充。XSLT把XML转变为其他的格式,如XHTML或WML,而CSS只是定义表达的样式。作为生成的XHTML的一部分,有时XSLT也可以生成一些样式元素来使线条变模糊。 建议写一些独立的CSS文件,来代替在XSLT样式表中嵌入大量关于字体、颜色和其它的样式元素。XSL转变产生的XHTML只是包含这些独立的CSS文件。这就使XHTML更小,同时简化了XSLT,也使得浏览器下载页面时速度更快。 同样的技术也适用于JavaScript,应该存放一些独立的文件而不是把它们直接嵌入到转变中去。 5、小心处理不间断空格。 作者提示:作为对读者的回应,我已经重写了这条技巧,加入了我最近学到的关于不间断空格的新知识。感谢广大读者的反馈。[编者提示:我们已经在文件的尾部为附加评论加入了一个读者反馈链接。] 不间断空格对于XHTML来说是一个很有用的特性,它可以阻止浏览器在文字中引入换行符。它同时使得强迫两个以上连续的空格成为可能;因为浏览器总是把普通的空格(和其他的白空格字符)序列处理成一个空格。这里是一个包含不间断空格的XHTML的例子: Aidan Burke 当人们创建XHTML网页时,他们通常是象上面显示的那样在他们的源文件中插入" "字符。所有的浏览器都会把这个字符序列翻译成一个不间断空格并准确显示。然而,当用XSLT生成XHTML时,处理的方式就不同了。 XSLT样式表必须是格式准确的XML。因为" "不是XML预先定义好的五个标签,它不能直接被包含在样式表中。比如,接下来的这个XSLT片就不管用: <!-- won\\\'t work... --> <xsl:text>Aidan Burke</xsl:text> 这种特征使XSLT程序员必须用一种略微不同的方式来使用这种特性: <xsl:text>Aidan Burke</xsl:text> 结果表明,所有的案例都工作得很好。当样式表的输出方式是"html"时,像Xalan这样的处理器会自动的把字符实体" "转变为序列" "。从网络浏览器的角度来看,这看起来和其它的不间断空格没什么两样。 这里有一个完整的XSLT样式表的例子就是这样做的 <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="html" encoding="UTF-8"/> <xsl:template match="/"> <xsl:text>Aidan Burke</xsl:text> </xsl:template> </xsl:stylesheet> 当用Xalan时,这个转变的输出看起来是这样的: Aidan Burke 这很好,因为浏览器知道如何显示" "。但很不幸,XSLT规范并没有要求XSLT处理器把" "转变为" "。你必须在碰到这个问题的任何时候,对你使用的XSLT处理器进行这项测试。 有些程序员不喜欢必须记住"160"代表不间断空格。所以他们在XSLT样式表的DTD子集中定义这样一个实体: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE xsl:stylesheet [ <!ENTITY nbsp " "> ]> <xsl:stylesheet version="1.0" ... 现在,可以用" "来代替" "。因为XML解析器在XSLT处理" "之前就把这个实体转变成了" ",这也就方便了样式表的作者。提醒一句:有些XML相关的工具会在见到DOCTYPE时试着验证XSLT样式表。因为DTD子集没有包含所有XSLT元素的定义,验证就会报告错误。 假如流行的XSLT处理器会自动把" "转变为" ",会有什么问题呢?问题就是,当样式表的输出是"xml"而不是"html"时就会出现错误。 当XSLT输出方式是"html"时,大多数XSLT处理器修改它们的输出并提供应网络浏览器。比如,象"<br />"这样的标签,是一个有效的XML,可能会被转变为"<br>"。这更适用于在比较老的浏览器,但却不是格式准确的XML。 XHTML是目前被国际互联网联盟所推荐的用于书写网页的格式。因为XHTML文档必须是格式准确的XML,XSLT样式表作者很可能希望在生成XHTML时用"xml"的输出格式而不是"html"。这是一个生成XHTML的XSLT样式表的第一部分: <?xml version="1.0" encoding="UTF-8"?>br> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" encoding="UTF-8"/> <xsl:template match="/"> <html xmlns="http://www.w3.org/1999/xhtml"> ...remainder omitted 当输出方式是"xml"时,Xalan不会把" "转变为" "。相反,它会在结果树中插入一个160的字符码。这在有些时候会引起问题。比如,图2是运行在Windows 2000上的IE 5.5浏览器的一张截图。注重在有趣的字母"A"上有一个符号: 下载这个例子试一下结果。 图2的下半部分显示了一个在大多数情况的都能正常工作的交换技术。下面介绍了它如何工作: <xsl:text disable-output-escaping="yes">&nbsp;</xsl:text> disable-output-escaping="yes"使XSLT处理器在生成结果树时不再把" "转变为字符码160。相反,它将字符序列" "保留而不变化。这样浏览器就可以准确的显示不间断空格。 我必须提醒你注重的是XSLT规范并没有要求XSLT处理器支持disable-output-escaping,所以任何人在使用这个技术之前,请先用特别的工具测试一下。 图2,用"xml"输出方式(上方)和用disable-output-escaping方式(下方)的结果对比 下面对上面提到的这些技术做个总结: 用" "字符实体来代表不间断空格。因为大多数的XSLT会把这个实体转变为字符序列" ",所以当输出方式是"html"时,这是可以准确工作的。XSLT规范并没有要求这样做,但Xalan这样做了。 定义一个" "实体并使用它。这和上一点有同样的效率,但对于样式表作者来说看起来更好一点。然而,某些工具也许会试图用不存在的DTD来验证样式表,这时可能会出现问题。 用<xsl:text disable-output-escaping="yes">&nbsp;</xsl:text>作为" "的第二个选择。这在输出方式是"xml"时特殊有用。XSLT规范并不要求处理器支持disable-output-escaping。 6、写XML生成器类。 为了应用XSL转变,就必须要把Java对象转变成一定的XML数据。这可以用以下的几个途径来完成: 1) 为每个类增加一个getXML()方式。 2) 写一个知道如何将特定对象转变为XML的XML生成器。 3) 用一个熟知的Java-to-XML API来自动的转变为XML。 第一种途径看起来可能象下面这样: public class Customer { public Element getXML(Document doc) { // use the DOM API to create an Element // representing this object ... } ... } 这种途径很轻易解释和理解,但却有一个要害的设计缺点。要害的问题在于特定的XML表示方式和每个类紧紧的绑在了一起。当需要一个新的XML表示时,就必须写新的方式。这意味着当加入越来越多的XML"视图"时,类也会越变越大。 不难想象这样的情况,就是希望对于一个对象有多于一个的XML表示。在一份显示有成百上千的客户概要报表中,只有每个客户的很少几个要害字段出现在XML数据中。而在一个客户的具体报表中,XML数据应该包含关于这个客户的所有信息。 第二种途径把XML的生成代码分离出来放在几个工具类里。一个关于客户的XML生成器看起来可能象下面这样的: public class CustomerDOMProducer { public static Element toXML(Customer cust, Document doc) { ... use the DOM API to create a fragment of XML } } 简朴的从Customer类中把XML生成代码去掉;要增加新的XML表达时就只是简朴的写附加的XXXDomProducer类就行了。这样甚至可能改为用象JDOM这样的non-DOM APIs,也不需要对Customer代码作任何改变了。 ----------------------------------------------------------------------------------------------------------------------- 更多关于JDOM的信息,不要错过Brett McLaughlin最近发行的《Java&XML, 2ndEdition》。 ----------------------------------------------------------------------------------------------------------------------- 第三种途径也值得提一下,是用一种产品把Java对象转变为XML。虽然这些类型的工具对于持续和与应用程序进行数据交换方面很好,但他们在XSL转变方面可能不是很理想。这是因为生成的XML可能比手写代码方案提供的复杂得多,潜在的也就导致了更为复杂的XSLT样式表。 7、假设Cookie是被禁止的。 Servlet API支持用HttpSession来跟踪会话。这使象购物车那样的技术成为可能。这个类的默认行为是依赖浏览器的cookie来鉴别每个用户,但是用户可以禁止cookie。 当浏览器的cookie被禁止时,我们的应用就必须依赖其他某种机制来鉴别用户。URL重写就是servlet API用到的这一技术。因为各种原因,URL重写并不是自动发生的。为了在cookie被禁止时支持会话跟踪,程序员必须记住对应用程序发出的每一个URL进行编码。这可以靠给每一个超链接、表单动作、或重定向URL加上一个jsessionid=nnnnn来完成。下面的表列举了有和没有验证标记的URL: 普通URL 编码后的URL <a href="mylink"> <a href="mylink;jsessionid=129j2fjs87l156"> <form action="mylink"> <form action="mylink;jsessionid=129j2fjs871156"> 当用户点击一个编码的超链接或提交一个编码的表单时,servlet容器可以通过检查jsessionid的值来确定他或她的身份。 在用XSLT生成XHTML时,由于验证标识是动态的而且每个用户的身份都不同,所以这个会话标识应该被嵌入到每一页中,它应该作为一个样式表的参数被传递。下面是怎样在每个XSLT样式表顶部定义这个参数的例子: <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- ********************************************************* ** global.sessionID : Used for URL-rewriting to implement ** session tracking without cookies. ********************************************************* --> <xsl:param name="global.sessionID"/> ... 在一个servlet端的应用程序中,Java代码用JAXP的Transformer类把这个会话标识传给XSLT处理器。它很智慧,只在cookie不起作用的情况下才这样做: protected void doGet( HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException { Transformer trans = ... // obtain Transformer from JAXP HttpSession session = req.getSession(true); // allow cookieless session tracking if (!req.isRequestedSessionIdFromCookie()) { String sessionID = session.getId(); trans.setParameter("global.sessionID", ";jsessionid=" + sessionID); } 回到XSLT端的应用程序中,这个global.sessionID参数可以在生成每一页时被加到超链接和表单动作中。这个技术在《Java and XSLT》的第8章"Additional Techniques"中作了完整的讲解。 8、把XSLT作为一个代码生成器使用。 虽然XSLT通常是用来做基于Web的转变,但它并不是被局限于用作XHTML的输出。XSLT可以将XML转变为任意的文本格式,这就使它成为很多类型代码的生成器和其他开发工具的一个理想的选择。 当用XSLT作为一个基本的代码生成器时,将它集中在重复的和高度结构化的应用上是最好的。很多跟EJB相关的应用是高度结构化和有点重复的,使的XSLT成为代码生成的一个理想的选择。 ------------------------------------------------------------------------------------------------------------------------- 期待O\\\'Reilly的《Enterprise Java Beans》第三版,因为9月就会发行。 ------------------------------------------------------------------------------------------------------------------------- 9、对于i18n用<xsl:import> 图3显示了怎样将XSLT样式表模块化来支持国际化: 图3、XSLT国际化 这是利用<xsl:import>特征的一个有趣的窍门。用<xsl:import>,一个样式表可以导入一个或多个其它的样式表。假如样式表"A"导入样式表"B",样式表"A"中定义的模块和变量优先于在样式表"B"中找到的。 特定语言的样式表看起来可能是这样的: <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:import href="common.xslt"/> <xsl:variable name="lang.pageTitle">Welcome to XSLT!</xsl:variable> </xsl:stylesheet> 通用的样式表可能是这样的: <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="html" encoding="UTF-8"/> <xsl:template match="/"> <html> <head> <title><xsl:value-of select="$lang.pageTitle"/></title> </head> ...etc 就象这里显示的相同,通用的样式表里不写给用户显示的详细文本(如页面标题)。相反,它依赖在特定语言的样式表中定义的变量。用这种方法,增加新的语言支持就只是创建一个新语言的样式表。 这异常类似于"ordinary Java"国际化,是用不同的属性文件定义语言相关的文本。 10、设立StreamSource来解析相关的URI。 看一看下面的JAXP代码(被强调的是有问题的部分): // Stream containing XML data InputStream xmlStream = ... // Stream containing XSLT stylesheet InputStream xsltStream = ... Source xmlSource = new StreamSource(xmlStream); Source xsltSource = new StreamSource(xsltStream); TransformerFactory transFact = TransformerFactory.newInstance(); Transformer trans = transFact.newTransformer(xsltSource); trans.transform(xmlSource, new StreamResult(System.out)); 现在假设这个XSLT样式表导入了另外一个样式表,如下所示: <xsl:import href="formatName.xslt"/> 当XSLT处理器不知道到哪里去找formatName.xslt时就会引起问题。在XML数据中包含了对其他文件的引用时,同样的问题也会发生。这段代码可以通过改变StreamSource对象的构造来解决: Source xmlSource = new StreamSource(xmlStream, "file:///C:/data/xml/"); Source xsltSource = new StreamSource(xsltStream, "file:///C:/data/xslt/); 第二个参数提供了包含有XML和XSLT文件的URI。现在,当XSLT处理器解析XML数据和XSLT样式表中的URI引用时,就知道到哪里去寻找了。 更多知识 XSLT并不是一门很难的语言,虽然它工作的方法与Java大不一样。埋头写样式表可能是克服初学时的困难的最好方式。这里有一些关于XSLT的附加资源: javaxslt_example.zip: 下载一个XSLT样式表、XML文件和简朴的JAXP转变程序的例子。 The latest XSLT specification 国际互联网联盟的最新XSLT规范。 Sun\\\'s Java API for XML Processing (JAXP) site: 为不同种类的XML解析器和XSLT处理器提供了一套标准的Java接口 Altova\\\'s XML Spy: 一个支持XSL转变的XML编辑器。 The SAXON XSLT processor: 来自Michael Kay的XSLT处理器。 The Xalan XSLT processor: 来自Apache 组织的XSLT处理器。 返回类别: 教程 上一教程: 存取程序状态的几种方式??Java I/O应用杂谈 下一教程: java常用的加密,解密,数字签名等API 您可以阅读与"使用Java与XSLT的10条技巧"相关的教程: · [个人原创]JAVA 开发工具Jcreator使用技巧总结 · 使用JAVA开发CORBA应用 · JAVA应用技巧-JAVA中的发声提示 · Eclipse 使用技巧 · 我学习使用JAVA的一点体会 |
![]() ![]() |
快精灵印艺坊 版权所有 |
首页![]() ![]() ![]() ![]() ![]() ![]() ![]() |