问题
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" />
回答1:
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:
Generate a new stylesheet from the current one, using XSLT.
Load the stylesheet as an XmlDocument object. Then locate the respective
<xsl:include>
elements and set theirhref
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.
回答2:
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.
回答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.
回答4:
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
回答5:
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.
来源:https://stackoverflow.com/questions/7939815/dynamically-include-other-xsl-files-in-xslt