14.5. XSLT

XSLT是一种用于XML的转换语言,并作为一种在web应用中使用的view层技术广为人知。 如果你的应用本来就要处理XML,或者模型数据可以很容易转化为XML,那么XSLT是一个很好的选择。 下面的内容展示了在一个Spring MVC 应用中如何生成XML格式的模型数据,并用XSLT进行转换。

14.5.1. 写在段首

这是一个很小的Spring应用的例子,它只是在Controller中创建一个词语列表,并将它们加至模型数据(model map)。 模型数据和我们的XSLT视图名一块儿返回。 请参考 第 13.3 节 “控制器” 中关于Spring MVC Controller接口的细节。 XSLT视图把词语列表转化为一段简单XML,等待后续转换。

14.5.1.1. Bean 定义

这是一个简单的Spring应用的标准配置。dispatcher servlet配置文件包含一个指向 ViewResolver的引用、URL映射和一个简单的实现了我们的词语生成逻辑的controller bean:

<bean id="homeController"class="xslt.HomeController"/>

它实现了我们的词语生成“逻辑”。

14.5.1.2. 标准MVC控制器代码

控制器逻辑封装在一个AbstractController的子类,它的handler方法定义如下:

protected ModelAndView handleRequestInternal(
    HttpServletRequest request,
    HttpServletResponse response) throws Exception {
        
    Map map = new HashMap();
    List wordList = new ArrayList();
        
    wordList.add("hello");
    wordList.add("world");
       
    map.put("wordList", wordList);
      
    return new ModelAndView("home", map);
}

到目前为止,我们还没有做什么特定于XSLT的事情。在任何一种Spring MVC应用中,模型数据都以同样的方式被创建。 现在根据应用的配置,词语列表可以作为请求属性加入从而被JSP/JSTL渲染,或者通过加入VelocityContext来被Velocity处理。 为了使用XSLT渲染它们,应该以某种方式把它们转化为XML文档。有些软件包能自动完成对象图到XML文档对象模型的转化。 但在Spring中,你有完全的自由度,能以任何方式完成从模型数据到XML的转化。 这可以防止XML转化部分在你的模型结构中占据太大的比重,使用额外工具来管理转化过程是一种风险。

14.5.1.3. 把模型数据转化为XML

为了从词语列表或任何其他模型数据创建XML文档,我们必须创建一个 org.springframework.web.servlet.view.xslt.AbstractXsltView 的子类, 通常我们也必须实现抽象方法 createXsltSource(..)s。其第一个参数即model Map。 下面是我们这个小应用中HomePage类的完整代码:

package xslt;

// imports omitted for brevity

public class HomePage extends AbstractXsltView {

    protected Source createXsltSource(Map model, String rootName, HttpServletRequest
        request, HttpServletResponse response) throws Exception {

        Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
        Element root = document.createElement(rootName);

        List words = (List) model.get("wordList");
        for (Iterator it = words.iterator(); it.hasNext();) {
            String nextWord = (String) it.next();
            Element wordNode = document.createElement("word");
            Text textNode = document.createTextNode(nextWord);
            wordNode.appendChild(textNode);
            root.appendChild(wordNode);
        }
        return new DOMSource(root);
    }

}

你可以在上述子类中定义一些传给转化对象的参数,它们由健值对(name/value pairs)构成, 其中参数名必须与XSLT模板中定义的 <xsl:param name="myParam">defaultValue</xsl:param> 一致。 为了指定这些参数,你需要覆写继承自AbstractXsltViewgetParameters() 方法并返回一个包含健值对的Map。 如果你需要从当前请求中获取信息,你可以选择覆写 getParameters(HttpServletRequest request) 方法。(这个方法只有Spring 1.1以后的版本才支持。)

比起JSTL和Velocity,XSLT对本地货币和日期格式的支持相对较弱。 基于这点,Spring提供了一个辅助类,你可以在 createXsltSource(..) 方法中调用它来获得这样的支持。 请参考 org.springframework.web.servlet.view.xslt.FormatHelper 类的Javadoc。

14.5.1.4. 定义视图属性

对于“写在段首”中的只有一个视图的情况来说,views.properties文件(或者等价的xml文件,如果你用一种基于XML的视图解析器的话,就像在上面的Velocity例子中)看起来是这样的:

home.class=xslt.HomePage
home.stylesheetLocation=/WEB-INF/xsl/home.xslt
home.root=words

这里你可以看到,第一个属性'.class'指定了视图类,即我们的HomePage,其中完成从模型数据到XML文档的转化。 第二个属性'stylesheetLocation'指定了XSLT文件的位置,它用于完成从XML到HTML的转化。 最后一个属性'.root'指定了用作XML文档根元素的名字,它被作为 createXsltSource(..) 方法的第二个参数传给HomePage类。

14.5.1.5. 文档转换

最后,我们有一段转换上述文档的XSLT代码。 正如在'views.properties'中看到的,它被命名为 'home.xslt',存放在war文件中的 'WEB-INF/xsl'目录下。

<?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" omit-xml-declaration="yes"/>

    <xsl:template match="/">
        <html>
            <head><title>Hello!</title></head>
            <body>
                <h1>My First Words</h1>
                <xsl:apply-templates/>
            </body>
        </html>
    </xsl:template>

    <xsl:template match="word">
        <xsl:value-of select="."/><br/>
    </xsl:template>

</xsl:stylesheet>

14.5.2. 小结

下面是一个简化的WAR目录结构,其中总结了上面提到的文件和它们在WAR中的位置:

ProjectRoot
  |
  +- WebContent
      |
      +- WEB-INF
          |
          +- classes
          |    |
          |    +- xslt
          |    |   |
          |    |   +- HomePageController.class 
          |    |   +- HomePage.class
          |    |
          |    +- views.properties
          |
          +- lib
          |   |
          |   +- spring.jar
          |
          +- xsl
          |   |
          |   +- home.xslt
          |
          +- frontcontroller-servlet.xml

你要确保classpath下存在XML解析器和XSLT引擎。JDK1.4默认已提供了这些,多数J2EE容器也提供了,但还是要警惕,它可能是一些错误的根源。