从命令行转换

从命令行运行转换时,使用XSLTC很有意义。虽然Xalan解释转换器也包含一个命令行机制,但它不能像XSLTC那样,将预编译过的字节码保存为translet,以留作稍后之用。

从命令行运行XSLTC分两个步骤:

1. 编译translet

2. 在数据上运行编译过的translet


注意:关于这个主题的更多信息,请参见http://xml.apache.org/xalan-j/xsltc_usage.html上的用法指南。


编译translet

为了将article3.xsl样式表编译成translet,请执行下列命令:

java org.apache.xalan.xsltc.cmdline.Compile article3.xsl 

注意:对于Java平台的1.3版,还需要包含合适的类路径设置,如编译并运行程序中所示。


结果为一个名为article3.class的类文件(translet)。

下面是编译translet时可以指定的一些参数。

java org.apache.xalan.xsltc.cmdline.Compile
    -o transletName -d directory -j jarFile
    -p packageName {-u stylesheetURI | stylesheetFile } 

其中:

·   -o transletName

指定了生成的translet类(输出类)的名称。后缀.class是可选的。如果没有,会被样式表参数自动添加到指定的名称中。

·   -d directory

指定了目标目录(默认为当前的工作目录)。

·   -j jarFile

将生成的translet类文件输出到一个名为jarFile.jarJAR文件中。使用该选项时,只创建JAR文件。

·   -p packageName

为生成的translet类指定了一个包名。

·   -u stylesheetURI

指定了具有URI,比如http://myserver/stylesheet1.xsl的样式表。

·   stylesheetFile

(无标志)样式表文件的路径名。

运行translet

为了在示例文件article3.xml上运行编译过的translet,请执行下列命令:

java org.apache.xalan.xsltc.cmdline.Transform 
    article3.xml article3 

注意:如果是在1.3版的Java平台上运行translet,还需要设置类路径,如编译并运行程序中所示。

该命令将当前目录添加到类路径中,以便能找到该translet。输出在System.out中。

下面是运行translet时可以指定的一些参数:

java org.apache.xalan.xsltc.cmdline.Transform
    {-u documentURI | documentFilename} 
    className [name=value...] 

其中:

·   -u documentURI

指定了具有URIXML输入文档。

·   documentFilename

XML输入文档指定了文件名。

·   className

执行转换的translet。(这里不能指定后缀.class,必须像在运行Java应用时那样省略它。)

·   name=value ...

可选的一个或多个样式表参数的集合,被指定为名称-值对。

 使用过滤器链连接各个转换

创建过滤器链有时很有用——所谓过滤器链是指很多XSLT转换的连接,在这个连接中一个转换的输出是下一个的输入。本节将向你展示如何做到这一点。

编写程序

首先编写一个程序来进行过滤。该示例将展示完整的源代码,但你可以选用正在使用的其中一个程序作为基础,以便让事情变得更容易。


注意:这里描述的代码包含在FilterChain.java中。


该示例程序包含能识别各个类的包位置的导入语句。

import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
 
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import org.xml.sax.XMLFilter;
 
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerConfigurationException;
 
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.sax.SAXResult;
 
import javax.xml.transform.stream.StreamSource;
import javax.xml.transform.stream.StreamResult;
 
import java.io.*; 

该程序还包含你过去常使用的标准错误处理程序。它们如下所示,也正是这样它们才同时被集中在一个地方。

}
catch (TransformerConfigurationException tce) {
  // Error generated by the parser
  System.out.println ("* Transformer Factory error");
  System.out.println("   " + tce.getMessage() );
 
  // Use the contained exception, if any
  Throwable x = tce;
  if (tce.getException() != null)
    x = tce.getException();
  x.printStackTrace();
}
catch (TransformerException te) {
  // Error generated by the parser
  System.out.println ("* Transformation error");
  System.out.println("   " + te.getMessage() );
 
  // Use the contained exception, if any
  Throwable x = te;
  if (te.getException() != null)
    x = te.getException();
  x.printStackTrace();
}
catch (SAXException sxe) {
  // Error generated by this application
  // (or a parser-initialization error)
  Exception  x = sxe;
  if (sxe.getException() != null)
    x = sxe.getException();
  x.printStackTrace();
}
catch (ParserConfigurationException pce) {
  // Parser with specified options can't be built
  pce.printStackTrace();
}
catch (IOException ioe) {
  // I/O error
  ioe.printStackTrace();
} 

在导入语句和错误处理之间,该程序的核心部分由如下代码组成:

public static void main (String argv[])
{
  if (argv.length != 3) {s
    System.err.println (
      "Usage: java FilterChain style1 style2 xmlfile");
    System.exit (1);
  }
 
   try {
    // Read the arguments
    File stylesheet1 = new File(argv[0]);
    File stylesheet2 = new File(argv[1]);
    File datafile = new File(argv[2]);
 
     // Set up the input stream
    BufferedInputStream bis = new
      BufferedInputStream(newFileInputStream(datafile));
    InputSource input = new InputSource(bis);
 
     // Set up to read the input file (see Note #1)
    SAXParserFactory spf = SAXParserFactory.newInstance();
    spf.setNamespaceAware(true);
    SAXParser parser = spf.newSAXParser();
    XMLReader reader = parser.getXMLReader();
 
     // Create the filters (see Note #2)
    SAXTransformerFactory stf =
      (SAXTransformerFactory)
        TransformerFactory.newInstance();
    XMLFilter filter1 = stf.newXMLFilter(
      new StreamSource(stylesheet1));
    XMLFilter filter2 = stf.newXMLFilter(
      new StreamSource(stylesheet2));
 
    // Wire the output of the reader to filter1 (see Note #3)
    // and the output of filter1 to filter2
    filter1.setParent(reader);
    filter2.setParent(filter1);
 
     // Set up the output stream
    StreamResult result = new StreamResult(System.out);
 
  // Set up the transformer to process the SAX events generated
  // by the last filter in the chain
    Transformer transformer = stf.newTransformer();
    SAXSource transformSource = new SAXSource(
      filter2, input);
    transformer.transform(transformSource, result);
  } catch (...) {
    ... 

注意:

1. 当前Xalan转换引擎需要一个能识别命名空间的SAX解析器。而XSLTC却无此要求。

2. 这个怪异的代码位只能由如下事实来解释:SAXTransformerFactory扩展了TransformerFactory,并添加了用于获取过滤器对象的方法。newInstance()方法是定义在TransformerFactory中的一个静态方法,它(很自然地)返回一个TransformerFactory对象。但实际上,它返回的是SAXTransformerFactory。所以,为了获得SAXTransformerFactory定义的额外方法,返回值必须被强制转换成实际的类型。

3. XMLFilter对象既是一个SAX读取器又是一个SAX内容处理程序。作为SAX读取器,它为任何已注册过的能接收SAX事件的对象生成SAX事件。作为内容处理程序,它消耗它的“父”对象生成的SAX事件。这个父对象也必然是一个SAX读取器。(要调用事件发生器,“父”对象必须在查看内部体系结构时有意义。而从外部视角来看,该名称看起来不太合适)。过滤器同时生成和消耗SAX事件的这一事实允许它们互相链接。

了解过滤器链的工作原理

上述代码展示了如何创建转换。 8-2应该有助于理解执行该转换时发生了什么。

Operation of Chained Filters

8-2  链状过滤器的运行

当创建转换器时,它是在SAXSource对象上传递的,该对象封装了一个读取器(这里是filter2)和一个输入流。同时我们还为转换器传递了一个到结果流的指针,其中它在结果流中定向它的输出。上图说明了调用转换器上的transform()时,究竟发生了什么。这些步骤的解释如下:

1. 转换器为filter2建立了一个内部对象以作为内容处理程序,并告诉它解析输入源。

2. 然后filter2将自己设置为filter1的内容处理程序,并告诉它解析输入源。

3. filter1然后再告诉parser对象解析输入源。

4. parser按照要求解析了输入源之后,就生成SAX事件并将其传递给filter1

5. 作为内容处理程序的filter1处理这些事件并完成它的转换。然后,filter1又作为SAX读取器(XMLReader),将SAX事件发送给filter2

6. filter2也一样,它向转换器的内容处理程序发送它的事件,该内容处理程序再生成输出流。

测试程序

为了测试该程序,需要在一小部分XML DocBook格式的基础上创建一个XML文件,并将其转换成这里所定义的ARTICLE格式。然后再应用这个ARTICLE样式表,以生成一个HTML版本。


注意:该例子使用docbookToArticle.xsl article1c.xsl来处理small-docbook-article.xml。结果为filterout.html(浏览器可显示的版本为small-docbook-article-xml.htmldocbookToArticle-xsl.html article1c-xsl.htmlfilterout-src.html)。关于DocBook文章格式的更好描述请参见O'Reilly Web页面。


首先创建一篇文章,该文章使用了XML DocBook格式的一个微小子集。

<?xml version="1.0"?>
<Article>
  <ArtHeader>
    <Title>Title of my (Docbook) article</Title>
  </ArtHeader>
  <Sect1>
    <Title>Title of Section 1.</Title>
    <Para>This is a paragraph.</Para>
  </Sect1>
</Article> 

下一步,创建一个样式表并将其转换成ARTICLE格式。

<xsl:stylesheet 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  version="1.0"
  >
  <xsl:output method="xml"/> (see Note #1)
 
   <xsl:template match="/">
    <ARTICLE>
      <xsl:apply-templates/>
    </ARTICLE>
  </xsl:template>
 
  <!-- Lower level titles strip element tag --> (see Note #2)
 
  <!-- Top-level title -->
  <xsl:template match="/Article/ArtHeader/Title"> (Note #3)
    <TITLE> <xsl:apply-templates/> </TITLE>
  </xsl:template>
 
   <xsl:template match="//Sect1"> (see Note #4)
    <SECT><xsl:apply-templates/></SECT>
  </xsl:template>
 
   <xsl:template match="Para"> 
    <PARA><xsl:apply-templates/></PARA> (see Note #5)
  </xsl:template>
 
</xsl:stylesheet> 

注意:

1. 这次,样式表生成的是XML输出。

2. 后面的模板(对于顶级的标题元素)只匹配主标题。对于节标题,TITLE标签被去除。(由于没有模板转换管理这些标题元素,它们都被忽略。而它们包含的文本节点仍被作为XSLT内置模板规则的结果被回送——所以只有标签被忽略,而不是文本。其他更多内容如下所示)。

3. 来自DocBook文章头部的标题成为了ARTICLE标题。

4. 有编号的节标签被转换成纯SECT标签。

5. 由于该模板执行了一个大小写转换,所以Para就变成了PARA

虽然没有明确提到,但XSLT事实上定义了很多内置的(默认)模板规则。完整列表如规范中的第5.8节所示。它们主要用于自动复制文本和属性节点,以及跳过注释和处理指令。它们还规定即使它们包含的标签没有模板,内部元素也要被处理。这就是即使节标题没有被任何模板转换,其中的文本节点也被处理的原因。

这时,运行FilterChain程序,并将上面的样式表(docbookToArticle.xsl)ARTICLE样式表(article1c.xsl)和小DocBook文件(small-docbook-article.xml)按照顺序传递给它。结果应该如下所示:

<html>
<body>
<h1 align="center">Title of my (Docbook) article</h1>
<h2>Title of Section 1.</h2>
<p>This is a paragraph.</p>
</body>
</html> 

注意:该输出是利用JAXP 1.0生成的。但是,该链中的第一个过滤器目前没有转换输入文件中的任何标签。除非这一缺陷得到修复,否则你看到的输出将由HTML输出中的连接纯文本组成,比如"Title of my (Docbook) article Title of Section 1. This is a paragraph."


结论

恭喜!你已经完成了本XSLT教程。利用XMLXSLT可以做很多事情,并且现在你可以开始探索更多正等待你去发现的有趣的可能性了。

更多信息

关于XSL样式表、XSLT和转换引擎的更多信息,请参见:

·   关于XSLT的一个不错的介绍,它以一个简单的HTML页面开始并使用XSLT来逐步自定义它: http://www.xfront.com/rescuing-xslt.html

·   可扩展的样式表语言(XSL): http://www.w3.org/Style/XSL/

·   XML路径语言: http://www.w3.org/TR/xpath

·   Xalan转换引擎: http://xml.apache.org/xalan-j/

·   XSLTC转换引擎: http://xml.apache.org/xalan-j/

·   XSLTC的使用技巧:http://xml.apache.org/xalan-j/xsltc_usage.html

·   利用XSLTC设计样式表以最大化性能:http://xml.apache.org/xalan-j/xsltc/xsltc_performance.html