在Java程序中处理XML到HTML、XML到文本或者其他XML结构的转换时,XSLT是一种非常成熟且表达力强的技术。很多时候我们并不希望把某些数值硬编码在XSLT文件里,比如当前用户名、系统时间、分页大小或者环境标识。这时候就可以利用javax.xml.transform.Transformer提供的setParameter方法,在转换执行之前把Java端的变量传递给XSLT模板,从而实现动态化渲染。

一、XSLT侧如何声明参数
要在XSLT中接收Java传来的参数,必须在样式表顶部使用xsl:param元素进行声明。这个声明通常放在xsl:stylesheet或者某个xsl:template的直接子节点位置。如果声明在根级别,那么整个样式表都能访问该参数;如果声明在某个具体模板内,则仅在该模板中有效。
参数声明时可以给定默认值,当Java端没有调用setParameter传入对应名称的值时,XSLT就会使用默认值继续执行,而不会报错。这种机制让我们在开发和测试阶段非常方便,不必每次都构造完整的调用环境。
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- 声明一个全局参数,默认值为guest -->
<xsl:param name="userName" select="'guest'"/>
<xsl:param name="maxCount" select="10"/>
<xsl:template match="/">
<html>
<body>
<h1>欢迎, <xsl:value-of select="$userName"/></h1>
<p>最大显示条数: <xsl:value-of select="$maxCount"/></p>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
二、Java侧使用setParameter传参
在Java代码中,我们首先通过TransformerFactory加载XSLT并生成Transformer实例。随后,在调用transform方法做实际转换之前,使用transformer.setParameter(name, value)把参数放进去。第一个参数是字符串,必须与XSLT里xsl:param的name属性严格一致;第二个参数可以是字符串、数字、布尔值,也可以是DOM节点等对象。
需要特别注意的是,一旦transform方法执行完毕,本次转换里的参数绑定就结束了。如果循环处理多个XML并希望传入不同参数,必须在每次transform前重新设置,或者为每次转换新建Transformer对象。另外,参数名是大小写敏感的,写成username和userName在XSLT看来是两个不同的变量。
import javax.xml.transform.*;
import javax.xml.transform.stream.StreamSource;
import java.io.StringReader;
import java.io.StringWriter;
public class XsltParamDemo {
public static void main(String[] args) throws Exception {
String xslt = "<?xml version='1.0'?>"
+ "<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>"
+ "<xsl:param name='userName' select=''guest''/>"
+ "<xsl:template match='/'><h1>Hi <xsl:value-of select='$userName'/></h1></xsl:template>"
+ "</xsl:stylesheet>";
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer(
new StreamSource(new StringReader(xslt)));
// 在转换前设置参数
transformer.setParameter("userName", "张三");
StringWriter writer = new StringWriter();
transformer.transform(
new StreamSource(new StringReader("<root/>")),
new StreamResult(writer));
System.out.println(writer.toString());
}
}
三、传递复杂对象与节点参数
除了传递字符串和数字,有时候我们需要把一段XML节点作为变量传给XSLT,以便进行结构化的比对或嵌套输出。这时可以把Java中构造好的DOM Node对象直接作为setParameter的第二个参数传入。XSLT内部就能像处理普通节点一样使用xsl:copy-of或者XPath进行遍历。
不过要注意,并非所有XSLT处理器都完美支持任意Java对象。标准做法是将数据先转成DOM节点,再传入。如果传了普通POJO,多数实现会调用其toString方法变成字符串,往往不符合预期。因此涉及层级数据时,显式构建Document片段是最稳妥的方案。
import org.w3c.dom.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamSource;
public class NodeParamDemo {
public static void main(String[] args) throws Exception {
DocumentBuilder db = DocumentBuilderFactory.newInstance()
.newDocumentBuilder();
Document doc = db.newDocument();
Element extra = doc.createElement("extra");
extra.setTextContent("附加信息");
doc.appendChild(extra);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer(
new StreamSource("template.xsl"));
// 传入DOM节点作为参数
transformer.setParameter("payload", doc.getDocumentElement());
transformer.transform(new DOMSource(doc),
new StreamResult(System.out));
}
}
四、常见误区与排错建议
初学者经常遇到参数传了但XSLT取不到值的情况。绝大多数原因都是名称不匹配,例如Java里写setParameter("user", ...)而XSLT里声明的是name="userName"。由于XSLT对未匹配的参数不会抛出异常,而是静默使用默认值,这个问题非常隐蔽。
另一个误区是认为setParameter可以在transform之后调用并影响结果,实际上参数绑定仅作用于当次转换调用。如果使用了带缓存的Templates对象来提升性能,也要注意从Templates.newTransformer()拿到的每个Transformer实例都是独立的,参数不会跨实例共享。
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
| 输出始终是默认值 | 参数名拼写不一致 | 核对xsl:param的name与setParameter首参 |
| 报非法类型错误 | 传入了不支持的Java对象 | 改为传入DOM节点或字符串 |
| 循环里参数串味 | 复用同一Transformer未重置 | 每次transform前重设或新建实例 |
五、小结
通过Transformer.setParameter向XSLT传参,是Java生态中解耦样式逻辑与运行数据的标准做法。只要在XSLT中用xsl:param声明、在Java侧转换前准确设置、并注意类型和作用域,就能轻松实现灵活可配的XML转换流程。
在真实项目中,可以把数据源查询条件、页面主题、多语言标识等都抽象成参数传入,既减少XSLT文件数量,也方便统一维护。掌握这一机制后,XSLT就不再只是一段静态脚本,而可以成为响应业务变化的动态渲染层。
JavaXSLTTransformer_setParameter修改时间:2026-08-02 08:48:35