如何基于YAML配置生成XSLT 3.0模板?附Java实现场景
解决方案:从YAML规则生成XSLT 3.0模板及模板调用问题处理
一、先明确规则到XSLT的映射逻辑
首先把YAML里的转换规则对应到标准XSLT模板的写法,这是解决问题的基础:
- 元素移除:生成空匹配模板,匹配到目标元素时不输出任何内容
<xsl:template match="要移除的元素名"/> - 元素重命名:匹配原元素,输出新元素名并继承其子节点/属性
<xsl:template match="原元素名"> <新元素名> <xsl:apply-templates select="@* | node()"/> </新元素名> </xsl:template> - 属性修改:匹配目标属性,输出新属性名和处理后的值
<xsl:template match="目标元素/@原属性名"> <xsl:attribute name="新属性名"> <xsl:value-of select="属性值处理逻辑(比如concat('prefix-', .))"/> </xsl:attribute> </xsl:template>
二、解决模板内调用模板的问题
根据你当前用Saxon s9api+扩展函数的场景,分两种方案处理:
方案1:静态生成完整XSLT文件(推荐)
既然已经用SnakeYAML把YAML解析成Java对象,直接在Java层拼接XSLT代码字符串,生成完整的.xsl文件,再用Saxon编译执行。这种方式完全规避动态模板调用的问题,因为所有模板都是预定义的静态结构。
示例Java代码片段:
// 假设transformations是解析后的YAML转换规则列表 StringBuilder xsltBuilder = new StringBuilder(); xsltBuilder.append("<xsl:stylesheet version='3.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>"); // 添加默认复制模板(保留未匹配的节点) xsltBuilder.append("<xsl:template match='@* | node()'><xsl:copy><xsl:apply-templates select='@* | node()'/></xsl:copy></xsl:template>"); for (Transformation rule : transformations) { switch (rule.getType()) { case "remove": xsltBuilder.append(String.format("<xsl:template match='%s'/>", rule.getMatch())); break; case "rename": xsltBuilder.append(String.format("<xsl:template match='%s'><%s><xsl:apply-templates select='@* | node()'/></%s></xsl:template>", rule.getMatch(), rule.getNewName(), rule.getNewName())); break; case "modify-attribute": xsltBuilder.append(String.format("<xsl:template match='%s'><xsl:attribute name='%s'><xsl:value-of select='%s'/></xsl:attribute></xsl:template>", rule.getMatch(), rule.getNewAttr(), rule.getNewValue())); break; } } xsltBuilder.append("</xsl:stylesheet>"); // 将xsltBuilder的内容写入文件,再用Saxon编译执行
方案2:运行时动态构建XSLT模板(适配你的s9api场景)
如果必须在运行时动态传递规则而不生成文件,直接用Saxon s9api的API动态创建XSLT模板节点,添加到样式表中,确保所有模板都是静态存在的,避免动态调用问题。
示例Java代码片段:
Processor processor = new Processor(false); XsltCompiler compiler = processor.newXsltCompiler(); // 基础样式表(包含默认复制模板) String baseXslt = "<xsl:stylesheet version='3.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>" + "<xsl:template match='@* | node()'><xsl:copy><xsl:apply-templates select='@* | node()'/></xsl:copy></xsl:template>" + "</xsl:stylesheet>"; XdmNode baseStylesheet = processor.newDocumentBuilder().build(new StringReader(baseXslt)); XsltPackage pack = compiler.compilePackage(baseStylesheet.asSource()); // 遍历转换规则,动态添加模板 for (Transformation rule : transformations) { if ("rename".equals(rule.getType())) { // 创建rename模板节点 Element template = processor.getUnderlyingConfiguration().getDocumentBuilder() .newElement(new QName("http://www.w3.org/1999/XSL/Transform", "template")); template.setAttribute("match", rule.getMatch()); Element newElement = processor.getUnderlyingConfiguration().getDocumentBuilder() .newElement(new QName(rule.getNewName())); Element applyTemplates = processor.getUnderlyingConfiguration().getDocumentBuilder() .newElement(new QName("http://www.w3.org/1999/XSL/Transform", "apply-templates")); applyTemplates.setAttribute("select", "@* | node()"); newElement.appendChild(applyTemplates); template.appendChild(newElement); // 添加到样式表顶层 pack.getPrincipalStylesheetModule().getContents().add(template); } else if ("remove".equals(rule.getType())) { // 创建空的remove模板 Element template = processor.getUnderlyingConfiguration().getDocumentBuilder() .newElement(new QName("http://www.w3.org/1999/XSL/Transform", "template")); template.setAttribute("match", rule.getMatch()); pack.getPrincipalStylesheetModule().getContents().add(template); } // 属性修改规则的动态创建逻辑类似 } // 生成可执行的XSLT并执行转换 XsltExecutable executable = pack.load(); XsltTransformer transformer = executable.newTransformer(); // 后续设置输入输出、执行转换...
方案3:在XSLT内直接处理规则(无需调用模板)
如果不想动态生成模板,也可以在XSLT模板内通过扩展函数获取规则,用<xsl:choose>分支直接执行转换逻辑,完全避免模板调用:
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="自定义扩展函数命名空间"> <xsl:template match="@* | node()"> <xsl:variable name="rule" select="my:get-transformation-rule(.)"/> <xsl:choose> <xsl:when test="$rule/@type = 'rename'"> <xsl:element name="{$rule/@new-name}"> <xsl:apply-templates select="@* | node()"/> </xsl:element> </xsl:when> <xsl:when test="$rule/@type = 'remove'"> <!-- 不输出内容 --> </xsl:when> <xsl:when test="$rule/@type = 'modify-attribute'"> <xsl:copy> <xsl:for-each select="@*"> <xsl:choose> <xsl:when test="local-name() = $rule/@match-attr"> <xsl:attribute name="{$rule/@new-attr}"> <xsl:value-of select="$rule/@new-value"/> </xsl:attribute> </xsl:when> <xsl:otherwise> <xsl:copy/> </xsl:otherwise> </xsl:choose> </xsl:for-each> <xsl:apply-templates select="node()"/> </xsl:copy> </xsl:when> <xsl:otherwise> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:otherwise> </xsl:choose> </xsl:template> </xsl:stylesheet>
内容的提问来源于stack exchange,提问作者I. Riahi
相关产品推荐
相关产品推荐

