在XSLT中动态包含其他XSL文件

时间:2011-10-29 15:34:20

标签: xslt markup xslt-1.0

我有一个小问题,有没有办法动态包含另一个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" />

5 个答案:

答案 0 :(得分:5)

  

我有一个小问题,有没有办法动态地包含另一个   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" />

href <xsl:include>属性中添加变量引用是违法的。根据W3C XSLT 1.0和XSLT 2.0规范,此属性的值必须是URI引用。

但是,如果在转换开始之前知道$PathToWeb变量的值,则可以通过多种方式使用它来动态生成样式表表示,其中<xsl:include>语句在上面包含所需的URI(在用$PathToWeb替换所需的值之后:

  1. 使用XSLT从当前生成新样式表。

  2. 将样式表加载为XmlDocument对象。然后找到相应的<xsl:include>元素,并将其href属性设置为所需的值。最后,使用代表样式表的如此修改的XmlDocument调用转换。

  3. 方法2.已在XPath Visualizer 中使用了11年,以动态设置用于选择用户输入的所有节点的select属性的确切值XPath表达式选择并生成表示XML文档的HTML文档,其中突出显示所有选定和可见节点。

答案 1 :(得分:5)

我已经以不同的方式解决了这个问题,对于使用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://stackoverflow.com/xsl"/>
    ...

URI解析器的resolve方法会将import://stackoverflow.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() ) &&
        "stackoverflow.com".equalsIgnoreCase( uri.getAuthority() ) ) {
      result = openTemplate();
    }
    else {
      result = getDelegate().resolve( href, base );
    }

    return result;
  }

添加openTemplate()方法,其中包含动态确定要打开的XSL文件的逻辑。

答案 2 :(得分:2)

你不能这样做。原因很简单:

XSL将首先在编译期间扩展xsl:include,然后再执行其他操作。此时,您的“变量”不知道且无法知道,并且一旦编译,您就无法更改已编译的变量。另外,href是统一资源定位器而不是XPath表达式,因此您不能只扩展其中的变量。

答案 3 :(得分:0)

在PHP中,与其他制度一样,使用XSL样式表是一个多步骤的过程:

1)从XSL文件创建SimpleXML或DOMDocument对象。

2)创建一个XSLTProcessor对象。

3)将XSL文档对象导入处理器对象。

4)对XML数据文件运行转换。

在1)之后,XSL能够在作为步骤3)的一部分编译之前被操纵。在这里,xsl:include元素可以根据需要动态插入根元素。

因此,要动态插入xsl:includes:

1.1)使用def update_in_place(self, request, original_bundle, new_data): """ Update the object in original_bundle in-place using new_data. """ """Right here I am checking if the parameter flaggedUserID is in the request (PATCH saves request data to request.body). If this is false then copy the new data into the query, otherwise jump to alter_deserialized_detail_data where I am handling the appending of the data from the request. """ if 'flaggedUserID' not in request.body: original_bundle.data.update(**dict_strip_unicode_keys(new_data)) # Now we've got a bundle with the new data sitting in it and we're # we're basically in the same spot as a PUT request. SO the rest of this # function is cribbed from put_detail. self.alter_deserialized_detail_data(request, original_bundle.data) kwargs = { self._meta.detail_uri_name: self.get_bundle_detail_data(original_bundle), 'request': request, } return self.obj_update(bundle=original_bundle, **kwargs) | Xpath | getElementById检查数据XML是否存在可能需要额外样式表的元素。

1.2)从XSL的XML对象的根元素动态创建getElementsByTagname元素。

那就是它。在步骤3),修改后的XSL XML对象将被编译,就像它从一开始就构建一样。

当然,在1.2),来自其他XSL文档对象的任何节点(不仅仅是xsl:includexsl:include)都可以添加到基本XSL文档对象中的任何节点,从而提供更精细的控制。但是,正确xsl:import构造所有XSL样式表应该可以更简单地插入xsl:template元素。

答案 4 :(得分:0)

我的2便士值得一个简单的(但有效的)替代方案(仅提供用于说明的伪代码。谨慎行事:)

方法概要: 另一种解决方案可以包括一个简单的包装器脚本(例如shell,bash脚本或其他)来调用主xsl,使用名称xslt模式,主xslt文件,一个简单(空白)静态指定的xslt文件。

在主xsl中,包含一个静态xsl文件,它将调用/加载所有动态包含的xslt。然后,主xsl将以2种模式运行:正常模式(未指定模式),它将加载包含在其自身中的扩展xsl文件,并在静态xls中处理任何输入文件,或执行其预期的任何好东西做。第二种模式,即预处理器模式,用于加载dyanimically指定的xsl实例/文件。此模式将作为主处理运行的预处理器阶段调用。主xslt的处理流程是使用指定的预处理器模式调用它,然后在指示的正常处理模式下再次调用它。

实施提示: 对于每个xlator,定义一个扩展名为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样式表将包含任何扩展名xslts。它可以在运行时通过编辑它(作为xml文档)动态更新,为扩展xsl样式表添加include语句。 例如

 <!-- 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文件列表(包括其路径) 然后它将打开ext_xsl_container文件作为xml文档,为每个扩展添加(追加选项或清除文件并添加新代码)语句:xsl,保存文件并退出

接下来,当你在正常执行模式下运行main xsl时,它将包含模板loadDynamicXsl,它将包含运行时指定的扩展xslt文件

创建一个简单的包装器脚本(例如bash或shell脚本),它将接受主xslt的参数,以及一个运行预处理器模式的选项。如果启用了预处理器模式的选项,脚本将只调用主xslt两次,并在第一次运行中启用预处理器模式,然后在正常模式下启用第二次调用