Apache Xalan-J 命令行工具详解

Apache Xalan-J 2.7.3 命令行工具逐参数实测:Process 变换、xsltc Compile 编译、Transform 执行 translet 三个工具的完整用法、全部参数真实回显与 JDK 17 上的兼容性坑。

Apache XalanApache XalanJava命令行XSLTLinux系统2026/10/16

Apache Xalan-J 命令行工具详解

目标:在 Ubuntu 22.04 服务器上不写一行 Java 代码,用 Xalan-J 自带的三个命令行工具完成 XSLT 变换与样式表预编译:org.apache.xalan.xslt.Process(变换)、org.apache.xalan.xsltc.cmdline.Compile(编译 translet)、org.apache.xalan.xsltc.cmdline.Transform(执行 translet)。全部参数与开关均逐条真实运行并转录回显。

一、环境信息

项值
操作系统Ubuntu 22.04.5 LTS(x86_64)
JDKOpenJDK 17.0.20.1
Xalan-J2.7.3(官方二进制包 xalan-j_2_7_3-bin.tar.gz)
$ java -cp /root/xalan-j_2_7_3/xalan.jar org.apache.xalan.xslt.Process -V -IN /root/xalan-lab/hello.xml -XSL /root/xalan-lab/hello.xsl | head -1
>>>>>>> Xalan Version Xalan Java 2.7.3, <<<<<<<

-V 打印的版本号来自 jar 内 Manifest(Implementation-Version: 2.7.3),同时也是 Maven Central 的最新 release。

二、工具概览

工具完整类名职责引擎
Processorg.apache.xalan.xslt.Process样式表变换主入口(解析→变换→输出一条龙)默认解释器引擎;-XSLTC 切 XSLTC
Compileorg.apache.xalan.xsltc.cmdline.Compile把 .xsl 编译成 translet(Java 字节码 .class)XSLTC 编译器
Transformorg.apache.xalan.xsltc.cmdline.Transform加载 translet 执行变换XSLTC 运行时

三个工具都在 xalan.jar 内。xalan.jar 的 Manifest 声明了 Main-Class: org.apache.xalan.xslt.Process,因此最简调用形式是 java -jar xalan.jar;本文统一用 java -cp <jar集> <完整类名> 形式,可显式控制类路径。所有示例工作目录为 /root/xalan-lab(内含 hello.xml、hello.xsl、employees.xml、employees2html.xsl、report-text.xsl、param-dept.xsl、recurse.xsl、importer.xsl、lib/common.xsl、ent.xml、assoc.xml、hello-dtd.xml、bad.xsl、conflict2.xsl、identity.xsl、exslt-test.xsl 及 MyResolver、NoOpEntityResolver、EchoContentHandler 三个辅助类)。

$ export CP=/root/xalan-j_2_7_3/xalan.jar:/root/xalan-j_2_7_3/serializer.jar:/root/xalan-j_2_7_3/xercesImpl.jar:/root/xalan-j_2_7_3/xml-apis.jar
$ export LANG=C.UTF-8

三、前置条件

安装与校验(从官方归档下载二进制包并校验 sha512,实测):

$ cd /root
$ curl -L -O --max-time 300 -w "HTTP %{http_code}, %{size_download} bytes, %{time_total}s\n" \
    https://archive.apache.org/dist/xalan/xalan-j/binaries/xalan-j_2_7_3-bin.tar.gz
HTTP 200, 13570932 bytes, 10.773798s
$ curl -L -O https://archive.apache.org/dist/xalan/xalan-j/binaries/xalan-j_2_7_3-bin.tar.gz.sha512
$ sha512sum xalan-j_2_7_3-bin.tar.gz
18373698936776e9ce5818e9ffbf4efe56c44d52ca094b85fa438a59528c369acb27d07afbc85295bbc44698876887616f65d7bc6364defa08b36561e8b3ddc7  xalan-j_2_7_3-bin.tar.gz
$ tar xzf xalan-j_2_7_3-bin.tar.gz

辅助类(供 -URIRESOLVER/-ENTITYRESOLVER/-CONTENTHANDLER 演示)需先编译进类路径:

$ cd /root/xalan-lab && javac -encoding UTF-8 -cp $CP -d classes MyResolver.java NoOpEntityResolver.java EchoContentHandler.java

四、Process:变换主工具

4.1 完整用法

无参数运行会输出分页帮助(每页停顿等回车,脚本中用管道喂入换行即可取全),真实回显如下:

$ printf '\n\n\n\n\n\n\n\n\n\n' | java -cp $CP org.apache.xalan.xslt.Process
Xalan-J command line Process class options:

			-Common Options-

   [-XSLTC (use XSLTC for transformation)]
   [-IN inputXMLURL]
   [-XSL XSLTransformationURL]
   [-OUT outputFileName]
   [-E (Do not expand entity refs)]
   [-EDUMP {optional filename} (Do stackdump on error.)]
   [-XML (Use XML formatter and add XML header.)]
   [-TEXT (Use simple Text formatter.)]
   [-HTML (Use HTML formatter.)]
   [-PARAM name expression (Set a stylesheet parameter)]
   [-MEDIA mediaType (use media attribute to find stylesheet associated with a document.)]
   [-FLAVOR flavorName (Explicitly use s2s=SAX or d2d=DOM to do transform.)] 
   [-DIAG (Print overall milliseconds transform took.)]
   [-URIRESOLVER full class name (URIResolver to be used to resolve URIs)]
   [-ENTITYRESOLVER full class name (EntityResolver to be used to resolve entities)]
(press <return> to continue)
   [-CONTENTHANDLER full class name (ContentHandler to be used to serialize output)]
   [-SECURE (set the secure processing feature to true.)]

			-Options for Xalan-

   [-QC (Quiet Pattern Conflicts Warnings)]
   [-TT (Trace the templates as they are being called.)]
   [-TG (Trace each generation event.)]
   [-TS (Trace each selection event.)]
   [-TTC (Trace the template children as they are being processed.)]
   [-TCLASS (TraceListener class for trace extensions.)]
   [-L use line numbers for source document]
   [-INCREMENTAL (request incremental DTM construction by setting http://xml.apache.org/xalan/features/incremental true.)]
   [-NOOPTIMIMIZE (request no stylesheet optimization processing by setting http://xml.apache.org/xalan/features/optimize false.)]
   [-RL recursionlimit (assert numeric limit on stylesheet recursion depth.)]

			-Options for XSLTC-

   [-XO [transletName] (assign the name to the generated translet)]
(press <return> to continue)
   [-XD destinationDirectory (specify a destination directory for translet)]
   [-XJ jarfile (packages translet classes into a jar file of name <jarfile>)]
   [-XP package (specifies a package name prefix for all generated translet classes)]
   [-XN (enables template inlining)]
   [-XX (turns on additional debugging message output)]
   [-XT (use translet to transform if possible)]

💡 技术拓展:这是 Xalan-J 最经典的入口工具,用法说明分三段列出:-Common Options-(两个引擎共用)、-Options for Xalan-(解释器引擎专属,含 -TT/-TCLASS 等)、-Options for XSLTC-(编译引擎专属,-XO/-XD/-XJ 等),两个引擎通过 -XSLTC 切换。段首缩进为三个制表符,均按实测原样保留。

4.2 -IN / -XSL / -OUT:基本变换

$ java -cp $CP org.apache.xalan.xslt.Process -IN hello.xml -XSL hello.xsl
<?xml version="1.0" encoding="UTF-8"?><message>Hello, Xalan-J! Your XSLT engine is Apache Software Foundation.</message>
$ java -cp $CP org.apache.xalan.xslt.Process -IN hello.xml -XSL hello.xsl -OUT out-process.xml
$ cat out-process.xml
<?xml version="1.0" encoding="UTF-8"?><message>Hello, Xalan-J! Your XSLT engine is Apache Software Foundation.</message>

不写 -OUT 时结果走标准输出。样式表方法为 html/text 时输出器自动切换,-XML/-HTML/-TEXT 三个开关可强制指定。

4.3 -HTML 与 -TEXT:输出格式器

$ java -cp $CP org.apache.xalan.xslt.Process -IN employees.xml -XSL employees2html.xsl -OUT out-process.html
$ cat out-process.html
<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>员工花名册</title>
</head>
<body>
<h1>员工花名册(共 3 人)</h1>
<table cellpadding="4" border="1">
<tr>
<th>工号</th><th>姓名</th><th>部门</th><th>月薪(元)</th>
</tr>
<tr>
<td>103</td><td>Carol Wang</td><td>Engineering</td><td align="right">25,000</td>
</tr>
<tr>
<td>101</td><td>Alice Zhang</td><td>Engineering</td><td align="right">22,000</td>
</tr>
<tr>
<td>102</td><td>Bob Li</td><td>Sales</td><td align="right">15,000</td>
</tr>
</table>
</body>
</html>
$ java -cp $CP org.apache.xalan.xslt.Process -IN employees.xml -XSL report-text.xsl
薪资报表(共 3 人)
1. Carol Wang  Engineering  25,000
2. Alice Zhang  Engineering  22,000
3. Bob Li  Sales  15,000
平均薪资: 20,666.67
$ java -cp $CP org.apache.xalan.xslt.Process -XML -IN hello.xml -XSL hello.xsl
<?xml version="1.0" encoding="UTF-8"?><message>Hello, Xalan-J! Your XSLT engine is Apache Software Foundation.</message>

4.4 -PARAM:样式表传参

param-dept.xsl 声明 <xsl:param name="dept" select="'*'"/> 并按部门过滤员工。传参与缺省两条路径实测:

$ java -cp $CP org.apache.xalan.xslt.Process -IN employees.xml -XSL param-dept.xsl -PARAM dept Engineering
筛选部门 = Engineering
101  Alice Zhang  22000
103  Carol Wang  25000
$ java -cp $CP org.apache.xalan.xslt.Process -IN employees.xml -XSL param-dept.xsl
筛选部门 = *

参数值是 XPath 表达式——传字符串要写成 -PARAM dept "'Engineering'",直接写 Engineering 在这里恰好可行是因为程序内部把它当表达式求值仍得到字符串。生产脚本建议引号写法。

4.5 -E:实体展开开关

ent.xml 内含内部实体 &who;(定义为 "Xalan-J")。用 identity.xsl 原样复制文档,对比开关前后的输出(实测两者一致):

$ java -cp $CP org.apache.xalan.xslt.Process -IN ent.xml -XSL identity.xsl
<?xml version="1.0" encoding="UTF-8"?><greeting>
  <name>Xalan-J</name>
</greeting>
$ java -cp $CP org.apache.xalan.xslt.Process -E -IN ent.xml -XSL identity.xsl
<?xml version="1.0" encoding="UTF-8"?><greeting>
  <name>Xalan-J</name>
</greeting>

💡 技术拓展:XSLT 数据模型中实体在解析阶段已被展开为文本节点,变换后再以实体形式输出需要特殊的序列化行为,因此 -E 在 XSLT 流水线上的可观测差异有限。它更多影响非变换的解析路径。此处如实记录:在本环境两个引擎上未观测到输出差异。

4.6 -MEDIA:用媒体类型选关联样式表

assoc.xml 的 xml-stylesheet PI 声明了 media="screen"。-MEDIA 命中与不命中两种情况(实测):

$ java -cp $CP org.apache.xalan.xslt.Process -MEDIA screen -IN assoc.xml
<?xml version="1.0" encoding="UTF-8"?><message>Hello, Xalan-J! Your XSLT engine is Apache Software Foundation.</message>
$ java -cp $CP org.apache.xalan.xslt.Process -MEDIA print -IN assoc.xml

(Location of error unknown)XSLT Error (javax.xml.transform.TransformerException): No stylesheet found in: assoc.xml, media= print
Exception in thread "main" java.lang.RuntimeException: No stylesheet found in: assoc.xml, media= print
	at org.apache.xalan.xslt.Process.doExit(Process.java:1157)
	at org.apache.xalan.xslt.Process.main(Process.java:1130)

未命中时报 No stylesheet found in: assoc.xml, media= print 并以退出码 1 结束。

4.7 -FLAVOR 与 -DIAG

$ java -cp $CP org.apache.xalan.xslt.Process -FLAVOR s2s -IN hello.xml -XSL hello.xsl
<?xml version="1.0" encoding="UTF-8"?><message>Hello, Xalan-J! Your XSLT engine is Apache Software Foundation.</message>
$ java -cp $CP org.apache.xalan.xslt.Process -FLAVOR d2d -IN hello.xml -XSL hello.xsl
<?xml version="1.0" encoding="UTF-8"?><message>Hello, Xalan-J! Your XSLT engine is Apache Software Foundation.</message>
$ java -cp $CP org.apache.xalan.xslt.Process -DIAG -IN employees.xml -XSL employees2html.xsl -OUT out-diag.html


 --------- Transform of employees.xml via employees2html.xsl took 102 ms

-FLAVOR s2s(SAX→SAX 流式)与 d2d(DOM→DOM)输出一致;-DIAG 输出整体耗时,是量化样式表性能最直接的开关。

4.8 -V:版本信息

$ java -cp $CP org.apache.xalan.xslt.Process -V -IN hello.xml -XSL hello.xsl | head -1
>>>>>>> Xalan Version Xalan Java 2.7.3, <<<<<<<
$ java -cp $CP org.apache.xalan.xslt.Process -V
>>>>>>> Xalan Version Xalan Java 2.7.3, <<<<<<<
Error: No stylesheet or input xml is specified. Run this command without any option for usage instructions.
Exception in thread "main" java.lang.RuntimeException: Error: No stylesheet or input xml is specified. Run this command without any option for usage instructions.
	at org.apache.xalan.xslt.Process.doExit(Process.java:1157)
	at org.apache.xalan.xslt.Process.main(Process.java:708)

-V 不带输入运行时打印版本后直接报错退出(退出码 1)——版本查询要接受这个附带报错,或改用 unzip -p xalan.jar META-INF/MANIFEST.MF | grep Implementation-Version。

4.9 -URIRESOLVER:自定义 URI 解析器

importer.xsl 引用 href="common.xsl",实际文件在 lib/ 子目录。MyResolver 把 href 重定向到 lib/ 下(实测回调与结果):

$ java -cp classes:$CP org.apache.xalan.xslt.Process -URIRESOLVER MyResolver -IN hello.xml -XSL importer.xsl
[MyResolver] href=common.xsl base=file:///root/xalan-lab/importer.xsl
[MyResolver] 命中: lib/common.xsl
<?xml version="1.0" encoding="UTF-8"?><page>
<footer>rendered-by=Apache Software Foundation</footer>
</page>

注意 -URIRESOLVER/-ENTITYRESOLVER/-CONTENTHANDLER 的类必须在类路径上,本例用 -cp classes:$CP。不传类名会报 Class file not found for option -URIResolver(实测)。

4.10 -ENTITYRESOLVER:外部 DTD 拦截

hello-dtd.xml 声明 <!DOCTYPE greeting SYSTEM "nonexistent.dtd">。无解析器时解析器试图读取该文件报错;挂上 NoOpEntityResolver 拦截后成功(实测):

$ java -cp classes:$CP org.apache.xalan.xslt.Process -IN hello-dtd.xml -XSL hello.xsl
(Location of error unknown)java.io.FileNotFoundException: /root/xalan-lab/nonexistent.dtd (No such file or directory)
$ java -cp classes:$CP org.apache.xalan.xslt.Process -ENTITYRESOLVER NoOpEntityResolver -IN hello-dtd.xml -XSL hello.xsl
[NoOpEntityResolver] publicId=null systemId=file:///root/xalan-lab/nonexistent.dtd
<?xml version="1.0" encoding="UTF-8"?><message>Hello, Xalan-J! Your XSLT engine is Apache Software Foundation.</message>

4.11 -CONTENTHANDLER:自定义结果序列化

EchoContentHandler 把变换结果的 SAX 事件逐个打印,验证结果确实经 SAXResult 通道(实测;注意文本被解析器缓冲切成多段 characters 事件):

$ java -cp classes:$CP org.apache.xalan.xslt.Process -CONTENTHANDLER EchoContentHandler -IN hello.xml -XSL hello.xsl
[Echo] startDocument
[Echo] startElement: message
[Echo] characters: Hello,
[Echo] characters: Xalan-J
[Echo] characters: ! Your XSLT engine is
[Echo] characters: Apache Software Foundation
[Echo] characters: .
[Echo] endElement: message
[Echo] endDocument

4.12 -SECURE:安全处理

$ java -cp $CP org.apache.xalan.xslt.Process -SECURE -IN employees.xml -XSL exslt-test.xsl
<?xml version="1.0" encoding="UTF-8"?><report>
<java-ext>(Location of error unknown)Extension function: '{http://xml.apache.org/xalan/java/java.lang.Math}sqrt' can not be invoked when the XMLConstants.FEATURE_SECURE_PROCESSING feature is set to true.
$ java -cp $CP org.apache.xalan.xslt.Process -SECURE -IN hello.xml -XSL hello.xsl
<?xml version="1.0" encoding="UTF-8"?><message>Hello, Xalan-J! Your XSLT engine is Apache Software Foundation.</message>

含 Java 扩展的样式表在扩展调用点被拦下(错误文本混入输出流,命令退出码仍为 0),普通样式表不受影响。运行不可信样式表时必须加此开关。

4.13 -QC:模板冲突告警抑制

conflict2.xsl 中两条 match="employee" 模板优先级相同(显式 priority="2")。实测输出:

$ java -cp $CP org.apache.xalan.xslt.Process -IN employees.xml -XSL conflict2.xsl
root
    B:Alice ZhangB:Bob LiB:Carol Wang
$ java -cp $CP org.apache.xalan.xslt.Process -QC -IN employees.xml -XSL conflict2.xsl
root
    B:Alice ZhangB:Bob LiB:Carol Wang

同优先级冲突由后声明者(B 模板)静默胜出,实测均未输出 Pattern conflict 告警——-QC 作为防御性开关保留,但不要指望它区分结果差异。注意输出的 B: 三段连写且无尾随换行。

4.14 -TT / -TG / -TS / -TTC:运行期追踪

$ java -cp $CP org.apache.xalan.xslt.Process -TT -IN hello.xml -XSL hello.xsl
null Line #0, Column #0: template match='/' 
file:///root/xalan-lab/hello.xsl Line #4, Column #35: template match='/greeting' 
<?xml version="1.0" encoding="UTF-8"?><message>Hello, Xalan-J! Your XSLT engine is Apache Software Foundation.</message>

-TS 追加选择事件、-TG 追加生成事件、-TTC 输出模板子节点级轨迹,三者的轨迹文本与变换结果在同一输出流上交错出现(实测 -TTC 会打出 file:///root/xalan-lab/hello.xsl Line #5, Column #14: message 这类行并与结果文本交替,适合人工排障、不适合自动化解析)。追踪开关仅解释器引擎支持——-XSLTC -TT 组合实测报错后仍以 XSLTC 完成变换:

$ java -cp $CP org.apache.xalan.xslt.Process -XSLTC -TT -IN hello.xml -XSL hello.xsl
The option -TT is not supported in XSLTC mode.
<?xml version="1.0" encoding="UTF-8"?><message>Hello, Xalan-J! Your XSLT engine is Apache Software Foundation (Xalan XSLTC).</message>

4.15 -L / -INCREMENTAL / -RL:诊断与行为调整

$ java -cp $CP org.apache.xalan.xslt.Process -L -IN hello.xml -XSL hello.xsl
<?xml version="1.0" encoding="UTF-8"?><message>Hello, Xalan-J! Your XSLT engine is Apache Software Foundation.</message>
$ java -cp $CP org.apache.xalan.xslt.Process -INCREMENTAL -IN hello.xml -XSL hello.xsl
<?xml version="1.0" encoding="UTF-8"?><message>Hello, Xalan-J! Your XSLT engine is Apache Software Foundation.</message>

-L(行号注入,供错误诊断引用源文档行号)与 -INCREMENTAL(增量 DTM 构造)在正常输出上无可见差异。-RL 限制模板递归深度,配合递归样式表 recurse.xsl 实测:

$ java -cp $CP org.apache.xalan.xslt.Process -RL 5 -PARAM n 50 -IN hello.xml -XSL recurse.xsl
(Location of error unknown)Template nesting too deep. nesting = 5, template name is countdown
$ java -cp $CP org.apache.xalan.xslt.Process -RL 5 -PARAM n 3 -IN hello.xml -XSL recurse.xsl
3 2 1 

💡 技术拓展:-RL 的保险丝实现在 Xalan 解释器内部(TransformerImpl 的 StackGuard),无界递归(如死循环模板)会被它拦下,阈值可通过 -RL 调整。注意实测中超限报错后进程退出码仍为 0——用退出码判断成败时需结合 stderr,或改用 API 侧捕获异常。

4.16 -EDUMP:出错转储

bad.xsl 引用未定义变量,触发错误并转储(实测):

$ java -cp $CP org.apache.xalan.xslt.Process -EDUMP dump.txt -IN employees.xml -XSL bad.xsl
file:///root/xalan-lab/bad.xsl; Line #4; Column #41; org.apache.xml.utils.WrappedRuntimeException: Could not find variable with the name of notdefined

Exception in thread "main" java.lang.RuntimeException: No xml-stylesheet PI found in: employees.xml
	at org.apache.xalan.xslt.Process.doExit(Process.java:1157)
	at org.apache.xalan.xslt.Process.main(Process.java:1130)
$ ls -l dump.txt
-rw-r--r-- 1 root root 144 Oct 16 00:51 dump.txt
$ cat dump.txt
javax.xml.transform.TransformerException: No xml-stylesheet PI found in: employees.xml
	at org.apache.xalan.xslt.Process.main(Process.java:803)

错误打印后 Process 继续尝试从输入 XML 找关联样式表,报 No xml-stylesheet PI found in: employees.xml 并以退出码 1 结束;dump.txt(144 字节)转储的是该 RuntimeException 的栈。-EDUMP 不带文件名时输出到 stderr。

4.17 -NOOPTIMIMIZE 与 -TCLASS:名存实亡的开关

$ java -cp $CP org.apache.xalan.xslt.Process -NOOPTIMIMIZE -IN hello.xml -XSL hello.xsl
Invalid option: -NOOPTIMIMIZE
<?xml version="1.0" encoding="UTF-8"?><message>Hello, Xalan-J! Your XSLT engine is Apache Software Foundation.</message>
$ java -cp $CP org.apache.xalan.xslt.Process -TCLASS org.apache.xalan.trace.PrintTraceListener -IN hello.xml -XSL hello.xsl
Invalid option: -TCLASS
Invalid option: org.apache.xalan.trace.PrintTraceListener
<?xml version="1.0" encoding="UTF-8"?><message>Hello, Xalan-J! Your XSLT engine is Apache Software Foundation.</message>

尽管用法帮助里列出了两者,2.7.3 的参数解析实测均报 Invalid option(-TCLASS 连同其参数值各报一条),随后照常完成变换。旧脚本若依赖这两个开关需改走 API 侧(features/optimize 属性、自实现 TraceListener 注册到 TransformerImpl——PrintTraceListener 等追踪类本身仍在 jar 内,unzip -l xalan.jar | grep trace 可见)。

4.18 XSLTC 组开关:-XSLTC / -XO / -XT

$ java -cp $CP org.apache.xalan.xslt.Process -XSLTC -IN hello.xml -XSL hello.xsl
<?xml version="1.0" encoding="UTF-8"?><message>Hello, Xalan-J! Your XSLT engine is Apache Software Foundation (Xalan XSLTC).</message>
$ java -cp $CP org.apache.xalan.xslt.Process -XSLTC -XO processtranslet -XSL hello.xsl
<?xml version="1.0" encoding="UTF-8"?>
$ ls -l processtranslet.class
-rw-r--r-- 1 root root 3277 Oct 16 00:51 processtranslet.class
$ java -cp $CP:. org.apache.xalan.xslt.Process -XT -IN hello.xml -XSL hello.xsl -DIAG
The option -XT can only be used with -XSLTC.
<?xml version="1.0" encoding="UTF-8"?><message>Hello, Xalan-J! Your XSLT engine is Apache Software Foundation.</message>


 --------- Transform of hello.xml via hello.xsl took 86 ms
$ java -cp $CP:. org.apache.xalan.xslt.Process -XSLTC -XT -IN hello.xml -XSL hello.xsl -DIAG
<?xml version="1.0" encoding="UTF-8"?><message>Hello, Xalan-J! Your XSLT engine is Apache Software Foundation (Xalan XSLTC).</message>


 --------- Transform of hello.xml via hello.xsl took 55 ms
  • -XSLTC 切换到编译型引擎(xsl:vendor 变为 Apache Software Foundation (Xalan XSLTC),执行的是 XSLTC 字节码路径);
  • -XO 名字 让 Process 编译 translet 后即退出(仅输出 XML 声明,产物为 .class);
  • -XT 优先复用类路径上的 translet——不带 -XSLTC 会明确拒绝后仍走解释器完成变换,组合使用实测可行。

五、Compile:样式表编译器

5.1 完整用法(实测无参数输出)

$ java -cp $CP org.apache.xalan.xsltc.cmdline.Compile
XSLTC version 1.4
SYNOPSIS
   java org.apache.xalan.xsltc.cmdline.Compile [-o <output>]
      [-d <directory>] [-j <jarfile>] [-p <package>]
      [-n] [-x] [-u] [-v] [-h] { <stylesheet> | -i }

OPTIONS
   -o <output>    assigns the name <output> to the generated
                  translet.  By default the translet name is
                  derived from the <stylesheet> name.  This option
                  is ignored if compiling multiple stylesheets.
   -d <directory> specifies a destination directory for translet
   -j <jarfile>   packages translet classes into a jar file of the
                  name specified as <jarfile>
   -p <package>   specifies a package name prefix for all generated
                  translet classes.
   -n             enables template inlining (default behavior better
                  on average).
   -x             turns on additional debugging message output
   -u             interprets <stylesheet> arguments as URLs
   -i             forces compiler to read stylesheet from stdin
   -v             prints the version of the compiler
   -h             prints this usage statement

5.2 基本编译与执行

$ java -cp $CP org.apache.xalan.xsltc.cmdline.Compile -o hello hello.xsl
$ ls -l hello.class
-rw-r--r-- 1 root root 3267 Oct 16 00:51 hello.class
$ java -cp $CP:. org.apache.xalan.xsltc.cmdline.Transform hello.xml hello
<?xml version="1.0" encoding="UTF-8"?><message>Hello, Xalan-J! Your XSLT engine is Apache Software Foundation (Xalan XSLTC).</message>

产物 hello.class 是标准的 Java 类文件(translet),可随应用分发——这是样式表"闭源"与预编译提速的常规做法。

5.3 -d 输出目录 / -n 内联 / -v 版本

$ mkdir -p translet-out
$ java -cp $CP org.apache.xalan.xsltc.cmdline.Compile -n -d translet-out -o helloinl hello.xsl
$ ls -l translet-out/
total 4
-rw-r--r-- 1 root root 3207 Oct 16 00:51 helloinl.class
$ java -cp $CP org.apache.xalan.xsltc.cmdline.Compile -v
XSLTC version 1.4
SYNOPSIS
   java org.apache.xalan.xsltc.cmdline.Compile [-o <output>]
      [-d <directory>] [-j <jarfile>] [-p <package>]
      [-n] [-x] [-u] [-v] [-h] { <stylesheet> | -i }
……(OPTIONS 全文同 5.1,此处略)……
Compiler errors:
  /root/xalan-lab/-v (No such file or directory)

-v 先打印版本与完整用法,随后把 -v 当样式表名编译并报 Compiler errors:(真实回显见上),实际以用法输出中的版本行为准。

5.4 -j 与 -p:打包与包名

$ java -cp $CP org.apache.xalan.xsltc.cmdline.Compile -j hello.jar -p com.demo hello.xsl
$ jar tf hello.jar
META-INF/MANIFEST.MF
com/demo/hello.class

六、Transform:translet 执行器

6.1 完整用法与参数顺序(重点)

$ java -cp $CP org.apache.xalan.xsltc.cmdline.Transform
SYNOPSIS 
   java org.apache.xalan.xsltc.cmdline.Transform [-j <jarfile>]
      [-x] [-n <iterations>] {-u <document_url> | <document>}
      <class> [<param1>=<value1> ...]

   uses the translet <class> to transform an XML document 
   specified as <document>. The translet <class> is either in
   the user's CLASSPATH or in the optionally specified <jarfile>.
OPTIONS
   -j <jarfile>    specifies a jarfile from which to load translet
   -x              turns on additional debugging message output
   -n <iterations> runs the transformation <iterations> times and
                   displays profiling information
   -u <document_url> specifies XML input document as a URL

注意 SYNOPSIS:文档在前、translet 类名在后(与 Compile 不同,Transform 无参数运行不打印版本行)。顺序写反的真实报错(实测):

$ java -cp $CP org.apache.xalan.xsltc.cmdline.Transform hello hello.xml
Translet errors:Cannot find class 'hello.xml'.

正确顺序 + 必须把当前目录放进类路径(translet 通过类加载器定位,hello.class 在 CWD 而类路径无 . 时同样找不到,实测):

$ java -cp $CP org.apache.xalan.xsltc.cmdline.Transform hello.xml hello
Translet errors:Cannot find class 'hello'.
$ java -cp $CP:. org.apache.xalan.xsltc.cmdline.Transform hello.xml hello
<?xml version="1.0" encoding="UTF-8"?><message>Hello, Xalan-J! Your XSLT engine is Apache Software Foundation (Xalan XSLTC).</message>

6.2 参数透传

尾部的 name=value 会注入样式表参数(实测):

$ java -cp $CP org.apache.xalan.xsltc.cmdline.Compile -o depttranslet param-dept.xsl
$ java -cp $CP:. org.apache.xalan.xsltc.cmdline.Transform employees.xml depttranslet dept=Engineering
筛选部门 = Engineering
101  Alice Zhang  22000
103  Carol Wang  25000

6.3 -j 从 jar 加载 translet:实测失败与绕行

按用法 -j <jarfile> 应能直接加载 5.4 节的 hello.jar。实测三种写法均报错:

$ java -cp $CP org.apache.xalan.xsltc.cmdline.Transform -j hello.jar hello.xml hello
Translet errors:Cannot find class 'hello'.
$ java -cp $CP org.apache.xalan.xsltc.cmdline.Transform -j hello.jar hello.xml com.demo.hello
Translet errors:Cannot find class 'com.demo.hello'.
$ java -cp $CP org.apache.xalan.xsltc.cmdline.Transform -j /root/xalan-lab/hello.jar hello.xml com.demo.hello
Translet errors:Cannot find class 'com.demo.hello'.

绕行方案(实测可用):把 jar 放进 -cp,类名带上 -p 生成的包名:

$ java -cp $CP:hello.jar org.apache.xalan.xsltc.cmdline.Transform hello.xml com.demo.hello
<?xml version="1.0" encoding="UTF-8"?><message>Hello, Xalan-J! Your XSLT engine is Apache Software Foundation (Xalan XSLTC).</message>

-j 的 jar 内类加载在 2.7.3 + JDK 17 环境实测失效(与官方用法说明不符,如实记录),生产脚本请采用上述绕行。

七、注意事项

  1. 退出码语义不一:-RL 递归超限实测退出码仍为 0;-EDUMP 与 -MEDIA 未命中实测退出码为 1。判断成败不能只看退出码,要结合 stderr 与输出文件。
  2. 类路径显式化:无论四个 jar 还是 translet 产物,-cp 显式写全最稳;xalan.jar 的 Manifest Class-Path 会隐式加载其所在目录的兄弟 jar,移动单个 jar 会造成"此处能跑、彼处缺类"。
  3. locale:export LANG=C.UTF-8,否则 HTML/文本输出的中文在控制台与重定向文件中表现不一致。
  4. 分页帮助:Process 无参数运行会分页等待回车(脚本中用 printf '\n\n...\n' | 管道喂入取全);Compile/Transform 无参数运行直接一次性输出用法,无需喂回车。
  5. 性能量化:-DIAG 输出整体耗时,配合 -XSLTC/-XT 对比解释器与 translet 的差异(本文实测同一 hello 变换:解释器 86ms,translet 55ms)。

八、常见问题

  1. -NOOPTIMIMIZE / -TCLASS 报 Invalid option? 2.7.3 真实行为(见 4.17),改走 API。
  2. Transform 报 Cannot find class 'hello.xml'? 参数顺序反了——文档在前、类名在后(见 6.1)。
  3. Transform 报 Cannot find class 'translet名'? 类路径缺 . 或 jar;把 CWD/产物目录放进 -cp。
  4. -j 加载 jar 失败? 2.7.3 + JDK 17 实测失效,绕行:jar 放 -cp + 带包名类名(见 6.3)。
  5. -V 单独运行报 No stylesheet? 正常现象,-V 打印版本后因缺少输入直接报错退出(见 4.8)。
  6. -SECURE 下扩展函数报错? 预期行为,安全处理会禁用 Java 扩展(见 4.12)。

九、参考链接

  1. 《Apache Xalan-J 介绍与选型指南》——解释器与 XSLTC 双引擎架构。
  2. 《Apache Xalan-J 在 Ubuntu 22.04 集成使用指南》——安装与类路径布局。
  3. 《Apache Xalan-J 2.7.3 配置参数详解》——命令行开关的 API 等价参数。
  4. Xalan-J 命令行工具文档:https://xalan.apache.org/xalan-j/commandline.html