在 XSLT 中动态包含其他 XSL 文件
Posted
技术标签:
【中文标题】在 XSLT 中动态包含其他 XSL 文件【英文标题】:Dynamically include other XSL files in XSLT 【发布时间】:2011-12-17 21:31:04 【问题描述】:我有一个小问题,有没有办法动态包含另一个 xsl?例如:
<xsl:variable name="PathToWeb" select="'wewe'"/>
<xsl:include href="http://$PathToWeb/html/xsl/head.xsl" />
<xsl:include href="http://$PathToWeb/html/xsl/navigation.xsl" />
<xsl:include href="http://$PathToWeb/html/xsl/promo.xsl" />
<xsl:include href="http://$PathToWeb/html/xsl/3columns.xsl" />
<xsl:include href="http://$PathToWeb/html/xsl/footer.xsl" />
【问题讨论】:
使用你不能这样做。原因很简单:
XSL 将首先在编译期间扩展 xsl:include,然后再执行其他任何操作。那时您的“变量”不知道也无法知道,并且一旦编译,您就无法更改编译的转换。此外,href 是统一资源定位器而不是 XPath 表达式,因此您不能只在其中展开变量。
【讨论】:
好的,但那很愚蠢......在php中一个简单的include()解决了这个问题......我真的需要这个,为什么要这样开发? 我不知道为什么但是 xslt != php 恐怕:) @Row Minds 这是您以“好的,但这很愚蠢”开头的回复的精确翻译。就是这样:“我相信每次我想吃一根香蕉时,我的嘴里都没有一根香蕉,这很愚蠢。当我吃 PHP 时,它很好吃”。技术monkey-hoot(在这里替换任何名称,可能是PHP)以您想要的方式实现包含这一事实绝不会导致技术不同的猴子叫声(在这里替换任何名称,可能是xsl)以不同方式实现包含.【参考方案2】:我有一个小问题,有没有办法动态包含另一个 xsl?例如:
<xsl:variable name="PathToWeb" select="'wewe'"/> <xsl:include href="http://$PathToWeb/html/xsl/head.xsl" /> <xsl:include href="http://$PathToWeb/html/xsl/navigation.xsl" /> <xsl:include href="http://$PathToWeb/html/xsl/promo.xsl" /> <xsl:include href="http://$PathToWeb/html/xsl/3columns.xsl" /> <xsl:include href="http://$PathToWeb/html/xsl/footer.xsl" />
在<xsl:include>
的href
属性中有变量引用是非法的。根据 W3C XSLT 1.0 和 XSLT 2.0 规范,该属性的值必须是一个 URI 引用。
但是,如果$PathToWeb
变量的值在转换开始之前已知,则可以以多种方式使用它来动态生成样式表表示,其中上述<xsl:include>
语句包含所需的 URI (将$PathToWeb
的引用替换为所需的值后:
使用 XSLT 从当前样式表生成新样式表。
将样式表加载为 XmlDocument 对象。然后找到相应的<xsl:include>
元素并将它们的href
属性设置为所需的值。最后,使用经过如此修改的表示样式表的 XmlDocument 调用转换。
方法 2. 已在 XPath Visualizer 中使用了 11 年,用于动态设置 select
属性的准确值,该属性用于选择用户输入的 XPath 表达式选择的所有节点并生成一个代表 XML 文档的 HTML 文档,其中突出显示了所有选定和可见的节点。
【讨论】:
我用 XMLSpy 凭经验确定<xsl:include>
不允许 XPath 表达式作为其 href
属性的值,而 <xsl:result-document>
允许。不过,我在规范中找不到任何支持这一点的东西。两者之间真的有区别,还是错误/专有扩展? (实际上我什至没有使用变量,只是像 'test' 这样的任何表达式。)
这不是错误。 <xsl:result-document>
的 href
属性允许 AVT,目的是允许生成多个结果文档。 <xsl:include>
和 <xsl:result-document>
之间有很大的不同。前者只能在编译时处理,后者在运行时处理。
我不知道有多少编程语言具有在执行期间修改程序的结构,所以如果允许动态 xsl:includes 会相当令人惊讶。至于规范,xsl:include 的规则是 <xsl:include href = uri-reference />
而 xsl:result-document 的规则是 <xsl:result-document href? = uri-reference
:这些花括号很重要,如 2.2 Notation 中所述。
当它的语法不正确时,这个答案如何得到 4 次赞成?
什么是“不正确的语法”?如果您的意思是问题中的引用-是的,这是非法的。但这是问题,不是答案,只是说明这是非法的。【参考方案3】:
我以不同的方式解决了这个问题,可能对使用 Java 和 XSLT 的人有用(此解决方案特定于使用 javax.xml.transform
包的人)。
XSLT 转换器工厂允许设置自定义 URI 解析器。说如果你的 XSLT 看起来像
<?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" version="4.0" encoding="UTF-8"/>
<xsl:include href="import://***.com/xsl"/>
...
URI 解析器的resolve
方法将获得import://***.com/xsl
作为href
参数。 import://
可以作为自定义包含的“特殊”标识符方案,因此您可以检测它并创建/返回指向必要文件的javax.xml.transform.Source
。例如:
TransformerFactory tf = TransformerFactory.newInstance();
URIResolver delegate = tf.getURIResolver();
tf.setURIResolver( new CustomURIResolver( delegate ) );
那么,在CustomURIResolver
里面:
public Source resolve( String href, String base )
throws TransformerException
Source result = null;
URI uri = null;
try
uri = new URI( href );
catch( Exception e )
throw new TransformerException( e );
// The XSLT file has a URI path that allows for a file to be included
// dynamically.
if( "import".equalsIgnoreCase( uri.getScheme() ) &&
"***.com".equalsIgnoreCase( uri.getAuthority() ) )
result = openTemplate();
else
result = getDelegate().resolve( href, base );
return result;
添加一个 openTemplate()
方法,其中包含动态确定要打开的 XSL 文件的逻辑。
【讨论】:
【参考方案4】:在 PHP 中,与在其他制度下一样,使用 XSL 样式表是一个多步骤的过程:
1) 从 XSL 文件创建 SimpleXML 或 DOMDocument 对象。
2) 创建一个 XSLTProcessor 对象。
3) 将 XSL 文档对象导入到处理器对象中。
4) 对 XML 数据文件运行转换。
在 1) 之后,可以在作为步骤 3) 的一部分编译之前对 XSL 进行操作。正是在这里,xsl:include 元素可以根据需要从根元素中动态插入。
因此,要动态插入 xsl:includes:
1.1) 使用Xpath
|getElementById
|getElementsByTagname
检查数据 XML 中是否存在您可能需要额外样式表的元素。
1.2) 从 XSL 的 XML 对象的根元素中动态创建 xsl:include
元素。
就是这样。在第 3 步),修改后的 XSL XML 对象将被编译,就好像它从一开始就是这样构建的。
当然,在 1.2) 中,可以将来自其他 XSL 文档对象的任何节点(不仅仅是 xsl:include
或 xsl:import
)添加到基本 XSL 文档对象中的任何节点,从而提供更精细的控制。但是,所有 XSL 样式表的正确 xsl:template
构造应该可以更直接地插入 xsl:include
元素。
【讨论】:
【参考方案5】:我在一个简单(但有效)的替代方案上价值 2 便士(仅提供伪代码用于说明。谨慎操作:)
方法概要: 另一种解决方案可以包括一个简单的包装脚本(例如 shell、bash 脚本或其他)来调用您的主 xsl、使用名称 xslt 模式、主 xslt 文件、一个简单(空白)静态指定的 xslt 文件。
在主 xsl 中,包含一个静态 xsl 文件,它将调用/加载所有动态包含的 xslt。然后主 xsl 将在 2 种模式下运行:正常模式(未指定模式),它将加载包含在自身中的扩展 xsl 文件,以及在静态 xls 中,并处理任何输入文件,或者做它打算做的任何好事做。第二种模式,预处理器模式,将用于加载动态指定的 xsl 实例/文件。此模式将作为主处理运行的预处理器阶段调用。主 xslt 的处理流程是使用指定的预处理器模式调用它,然后使用指定的正常处理模式再次调用它。
实施提示: 为每个 xlator 定义一个 n 扩展名 xslt 文件 ext_xsl_container ,其目的是包含任何扩展名 xslt。 例如
<xsl:stylesheet >
<!-- main xslt -->
<xsl:import href="../xsl/ext_xsl_container.xsl/>
<!--param: list of dynamically specified extension xsl -->
<xsl:param name="extXslUrlList"/>
<!--param:preprocessor mode flag, with default set to false -->
<xsl:param name="preProcModeLoadXslF" select="false()" type="xs:boolean"
<!-- param: path to the staticall included ext_xsl_container: with default value set -->
<xsl:param name="extXslContainerUrl" select="'../xsl/ext_xsl_container.xsl'"/>
<xsl:if test=" ($preProcModeLoadXslF=true())" >
<xsl:call-template name="loadDynamicXsl" mode="preprocess_load_xsl"
</xsl:if>
....
</xsl:stylesheet>
ext_xslt_container 样式表将包含任何扩展 xslt。它可以在运行时通过编辑它(作为 xml 文档)动态更新,为扩展 xsl 样式表添加包含语句。 例如
<!-- ext xsl container : ext_xsl_container.xsl-->
<xsl:stylesheet
<xsl:include href="ext_xsl_container.xsl"/>
....
</xsl:stylesheet
创建一个小模板,比如 template_load_ext_xsl,指定模式,比如 mode="preprocess_load_xsl" 例如
<xsl:template name="loadDynamicXsl" mode="preprocess_load_xsl">
<!-- param: path to the staticall included ext_xsl_container-->
<xsl:param name="extXslContainerUrl"/>
<!--param: list of dynamically specified extension xsl -->
<xsl:param name="extXslUrlList"/>
<!-- step 1, [optional ] open the ext Xsl container file -->
<!-- step 2 [optional] clear contexts of the ext X -- >
<!-- step3 compile a list of include elements, one per each ext Xsl file -->
<!-- step 4 [optional] create a union of the include elements created with the content of the xsl container file : ie append content >
<!-- step 5 : write the union list of incudes to the ext XSL container file -->
<!-- DONE --->
</xsl:template>
模板将接受 ex_xsl_container 的名称和扩展 xsl 文件列表(包括它们的路径)作为参数 然后它将作为 xml 文档打开 ext_xsl_container 文件,为每个扩展添加(附加选项或清除文件并添加新代码)语句:xsl,保存文件并退出
接下来,当您以正常执行模式运行主 xsl 时,它将包含模板 loadDynamicXsl,该模板又将包含在运行时指定的扩展 xslt 文件
创建一个简单的包装脚本(例如 bash 或 shell 脚本),它将接受主 xslt 的参数,以及运行预处理器模式的选项。如果启用了预处理器模式选项,脚本将简单地调用主 xslt 两次,并在第一次运行时启用预处理器模式,然后在正常模式下进行第二次调用
【讨论】:
以上是关于在 XSLT 中动态包含其他 XSL 文件的主要内容,如果未能解决你的问题,请参考以下文章
xslt 不会选择在 XSLT 转换中动态更改名称空间以进行进一步转换