9

I have a small problem, is there a way to dynamically include another xsl? For example:

<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" />
Lukas Eder
  • 211,314
  • 129
  • 689
  • 1,509
Master345
  • 2,250
  • 11
  • 38
  • 50

5 Answers5

7

I have solved this problem differently, might be useful for someone who works with Java and XSLT (this solution is specific to people using javax.xml.transform package).

XSLT transformer factory allows setting a custom URI resolver. Say if your XSLT looks like

<?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"/>
    ...

The URI resolver's resolve method will get import://stackoverflow.com/xsl as a href parameter. import:// could serve as a "special" identifier scheme for custom includes, so you can detect it and create/return javax.xml.transform.Source which is pointing to the necessary file. For example:

TransformerFactory tf = TransformerFactory.newInstance();
URIResolver delegate = tf.getURIResolver();
tf.setURIResolver( new CustomURIResolver( delegate ) );

Then, inside 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;
  }

Add an openTemplate() method that includes the logic to dynamically determine the XSL file to open.

mindas
  • 26,463
  • 15
  • 97
  • 154
6

I have a small problem, is there a way to dynamically include another xsl? For example:

<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" />

It is illegal to have a variable reference in the href attribute of <xsl:include>. According to the W3C XSLT 1.0 and XSLT 2.0 specifications, the value of this attribute must be an URI reference.

However, if the value of the $PathToWeb variable is known before the start of the transformation, it can be used in a number of ways to produce dynamically a stylesheet representation in which the <xsl:include> statements above contain the desires URIs (after substituting the reference to $PathToWeb with the required value:

  1. Generate a new stylesheet from the current one, using XSLT.

  2. Load the stylesheet as an XmlDocument object. Then locate the respective <xsl:include> elements and set their href attributes to the desired values. Finally, invoke the transformation using the so modified XmlDocument that represents the stylesheet.

Method 2. has been used for 11 years in the XPath Visualizer to dynamically set the exact value of a select attribute used to select all nodes that a user-entered XPath expression selects and to generate an HTML document representing the XML document with all selected and visible nodes highlighted.

Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431
  • 1
    I determined empirically with XMLSpy that `` does not allow an XPath expression as the value of its `href` attribute, while `` does. I can't find anything in the spec to support this, though. Is there really a difference between the two, or is it a bug / a proprietary extension? (I'm not actually even using a variable, just any expression like {'test'}.) – Dabbler Oct 29 '11 at 16:15
  • 1
    It isn't a bug. AVTs are allowed for the `href` attribute of `` with the purpose to allow multiple result documents to be generated. There is a big difference between `` and ``. The former can only be processed at compile time, the latter is processed at run-time. – Dimitre Novatchev Oct 29 '11 at 16:31
  • 3
    I don't know of many programming languages with constructs that modify the program during execution, so it would be rather surprising if dynamic xsl:includes were allowed. As for the spec, the rule for xsl:include is `` while that for xsl:result-document is ` – Michael Kay Oct 29 '11 at 18:01
  • How's this answer got 4 upvotes when it's not the correct syntax? – Reza S Nov 25 '13 at 23:13
  • 2
    What is "not the correct syntax"? If you mean the quote from the question -- yes, this is illegal. But this is the *question*, not the answer, which just explains that this is illegal. – Dimitre Novatchev Nov 25 '13 at 23:32
  • fair enough :) I guess I was mostly disappointed that I can't use that syntax – Reza S Nov 25 '13 at 23:55
3

You cannot do this. The reasons are simple :

XSL will first expand the xsl:include during compilation, before it does anything else. At that point your "variable" is not know and cannot be known and you can't change the compiled transform once it compiles. In addition the href is a Uniform Resource Locator not an XPath expression, therefore you can't just expand a variable in it.

FailedDev
  • 26,680
  • 9
  • 53
  • 73
  • ok, but that is stupid ... in php a simple include() solved this problem ... and i really need this, why is developed this way? – Master345 Oct 29 '11 at 15:40
  • I don't know why but xslt != php I am afraid :) – FailedDev Oct 29 '11 at 15:42
  • 1
    @Row Minds This is an exact translation of your response that started with "ok, but that is stupid". Here is it: "I believe it is stupid that a banana is not in my mouth exactly and every time I want to eat a banana. When I eat PHP, it is delicious". The fact that technology monkey-hoot (substitute any name here, perhaps PHP) implements an include in the fashion you desire in no way makes it wrong that technology different-monkey-hoot (substitute any name here, perhaps xsl) implements an include differently. – DwB Oct 29 '11 at 15:46
0

In PHP, as under other regimes, it is a multi-step process to use an XSL stylesheet:

1) Create a SimpleXML or DOMDocument object from an XSL file.

2) Create an XSLTProcessor object.

3) Import the XSL document object into the processor object.

4) Run a transform on an XML data file.

After 1), the XSL is able to be manipulated before being compiled as part of step 3). It is here that xsl:include elements can be dynamically inserted off the root element as required.

Therefore, to dynamically insert xsl:includes:

1.1) Use Xpath|getElementById|getElementsByTagname to examine the data XML for the existence of elements for which you might require extra stylesheets.

1.2) Dynamically create xsl:include elements off the root element of the XSL's XML object.

That's it. At step 3), the modified XSL XML object will be compiled as if it was built that way from the start.

Of course, at 1.2), ANY nodes (not just xsl:include or xsl:import) from other XSL document objects can be added to ANY nodes in the base XSL document object, giving much finer control. However, proper xsl:template construction of all the XSL stylesheets should make it much more straightforward to just insert xsl:include elements.

Patanjali
  • 893
  • 13
  • 17
0

My 2 pence worth on a simple ( but effective ) alternative ( only psuedocode provided for illustration. proceed with caution :)

Outline of the approach: An alternative solution can consist of a simple wrapper script ( eg shell , bash script or other) to invoke your main xsl, use of name xslt modes, the main xslt file, a simple (blank) statically specified xslt file .

In the main xsl, include a static xsl file , that will call/load all the dynamically included xslt. The main xsl will then operate in 2 modes: the normal mode(unspecified mode), where it will load extension xsl files included in itself , and in the static xls, and process any input files, or do what ever good stuff its intended to do. The second mode, preprocessor mode , will be intended for loading the dyanimically specified xsl instances/files. This mode will be invoked as a preprocessor stage for the main processing run. The process flow for the main xslt would be to call it with the preprocessor mode specified, and then to call it again with the normal processing mode indicated.

Implementation hints : For each xlator define a n extension xslt file, ext_xsl_container , whose purpose is to include any extension xslt. eg

    <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>

The ext_xslt_container style sheet will include any extension xslts. It can be dynamically updated at run time by editing it ( as an xml document ) , adding include statement for extension xsl stylesheets. eg

 <!-- ext xsl container : ext_xsl_container.xsl--> 
<xsl:stylesheet
    <xsl:include href="ext_xsl_container.xsl"/>

    ....
</xsl:stylesheet 

Create a small template , say template_load_ext_xsl, with an assigned mode , say mode="preprocess_load_xsl" eg

<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>

The template will take as arguments, the name of the ex_xsl_container, and a list of extension xsl files ( including their paths) it will then open the ext_xsl_container file as an xml document , add ( options to append, or clear file and add new code ) statements for each extension : xsl, save the file and exit

Next when you run the main xsl in normal execution mode , it willl include the template loadDynamicXsl, which will inturn include the extension xslt files specifed at run time

Create a simple wrapper script ( eg bash , or shell script) that will take in arguments to the main xslt, and a option to run the preprocessor mode. The script will simply call the main xslt twice , if the option for the preprocessor mode is enabled , and enabling the preprocessor mode in the first run , followed by a 2nd call in normal mode

young chisango
  • 103
  • 1
  • 6