XSLT conditional inclusion of an external file

I want to do conditional inclusion in XSLT, but xsl: include is a top-level element. You can only use xsl: if or xsl: select inside the template. Is there any hack or work around that allows you to conditionally include an external file? I tried to use the document () function, but I was not able to load my external file (perhaps because it does not correspond to some set of rules that will make it "valid").

My external XML file is a bunch of xslt code fragments. depending on the value of the variable in the main XSLT file, the code from the external file must be “copied / pasted” into place (for example, conditional inclusion in C or PHP).

The flow of my main XSLT file should run as follows:

$configurationMode if ( $configurationMode = Standard ) { xsl:include="standard.xml" } else { xsl:include="alt.xml" } 

Obviously, I cannot do this as simply as above, so I ask if there is a hack or workaround.

+6
source share
8 answers

This cannot be done using XSLT 1.0 and can be done (to a very limited extent) in XSLT 2.0 using use-when .

There are non-xslt ways to achieve the desired dynamic change to the xsl:include or xsl:import directive.

One such method is to load the XSLT stylesheet as an XmlDocument and use the available DOM methods to access and modify the attributes to set the href attribute to the desired value. Then initiate the conversion from this modified XMLDocument XSLT stylesheet modified in memory.

+6
source

As I understand it, "include" occurs when the xml parser parses and compiles the stylesheet. This means that no logic or evaluation of the expression can occur before the inclusion is processed, and therefore there is no way to make it conditional as such. You need to make conditional behavior outside the stylesheet.

Take a look at this http://www.dpawson.co.uk/xsl/sect2/N4760.html#d6065e100

In particular, this comment by Mike Kay helps:

This has been raised several times. In the previous thread, we came to the conclusion that the user tried to write a universal style sheet G, and then specialize it, conditionally, including a special style sheet A or B. The way to satisfy this requirement is for A and B to include G, and not vice versa, and then you Conditionally select A or B as the main style sheet when starting the conversion.

+3
source

Try to invert the structure: if you have two special target modules pink.xsl and blue.xsl and a universal module baby.xsl, instead of trying to import / include one of pink.xsl or blue.xsl in baby.xsl, instead, use pink.xsl or blue.xsl as the top-level entry stylesheet, and each of these two imports baby.xsl. This is the way it was intended to be used, it is for hacking or a workaround.

Alternatively, given this description of your scenario: “My external xml file is a bunch of xslt code fragments”, a more suitable approach in your case could be to compile a stylesheet from these fragments as a separate step, using, for example, XSLT tranformation than using xsl: include / xsl: import.

+3
source

Not sure if this applies to your scenario, but I will throw it away anyway.

I have done similar things in the past, but instead of including conditional inclusion, I call the template in either of the two files based on what xsl: is in the evaluation. If you do not want to use templates in your case, do not take this answer into account.

For example, let's say that the "parent" xslt file is called root.xslt , and the two conditionally used files are child1.xslt and child2. XSLT . Let's say I want to run conditional logic regarding the status value of a node. If the value is "CURRENT", I want to call a template called isCurrent in child1.xslt, otherwise I will call a template called isNotCurrent in child2.xslt. For brevity, in each case, I just pass the template to the root node and all the children.

It will look something like this:

 <?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"> <xsl:import href="child1.xslt"/> <xsl:import href="child2.xslt"/> <xsl:template match="/"> <xsl:choose> <xsl:when test="rootNode/status = 'CURRENT'"> <!-- template isCurrent defined in child1.xslt --> <xsl:apply-templates select="rootNode" mode="isCurrent" /> </xsl:when> <xsl:otherwise> <!-- template isNotCurrent defined in child2.xslt --> <xsl:apply-templates select="rootNode" mode="isNotCurrent" /> </xsl:otherwise> </xsl:choose> </xsl:template> </xsl:stylesheet> 

Hope this at least helps a bit.

+1
source

In addition to what has already been said, a possible solution would be to make additional files available that contain XML files (instead of XSLT files). That way you can enable them using the XPath document() function (which will be evaluated at runtime, not compile time).

You can then change the behavior of your transform based on the contents of the loaded XML document; however, you cannot provide XSLT executable snippets in included documents.

It depends on your use case, whether it is a solution - if additional documents have a strong influence on the control flow of your conversion, you do not want to define them in plain XML, because you implement something like XSLT. However, when your documents serve as configuration files, you might consider providing them with a simple XML document.

If you are having problems with document() , use the XML validator in your files. An error means your files are not valid XML.

0
source
 <xsl:variable name="CONDITIONAL" select="ELEMENT/CONDITION"/> <xsl:variable name="DOCUMENTNAME" select="document(concat($CONDITIONAL,'-DOCUMENT.xml'))/ELEMENT"/> 

In this example, I made conditional use of an external file. I had a flag in basic XML that XSL uses as a variable to specify the name of another file to be pulled into the template. Once this variable is defined, as in my case, I use it to pull other XML data into the generated output. The concat command combines the data to get the file name.

So, when I want to get information from an external XML file, I use '$ DOCUMENTNAME' to reference external data.

0
source

With the addition of static parameters , this is now possible in XSLT 3.0. Static parameters can be used in the use-when xsl:include attribute.

Now we can declare the parameters with the default values false() , and then override the ones we need at runtime ...

 <xsl:param name="someparam" as="xs:boolean" select="false()" static="yes" required="no"/> <xsl:include href="include_me.xsl" use-when="$someparam"/> 

Here is a complete working example tested with Saxon-HE v9.7 (also tested with Saxon-PE 9.5).

XML input (test.xml)

 <doc> <foo/> </doc> 

Core XSLT 3.0 (test_main.xsl)

 <xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs"> <xsl:output indent="yes"/> <xsl:strip-space elements="*"/> <xsl:param name="inc1" as="xs:boolean" select="false()" static="yes" required="no"/> <xsl:param name="inc2" as="xs:boolean" select="false()" static="yes" required="no"/> <xsl:include href="test_inc1.xsl" use-when="$inc1"/> <xsl:include href="test_inc2.xsl" use-when="$inc2"/> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> </xsl:stylesheet> 

Perhaps the first inclusion of XSLT 3.0 (test_inc1.xsl)

 <xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="foo"> <xsl:copy>INCLUDE FILE 1!!!</xsl:copy> </xsl:template> </xsl:stylesheet> 

The second possible inclusion of XSLT 3.0 (test_inc2.xsl)

 <xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="foo"> <xsl:copy>INCLUDE FILE 2!!!</xsl:copy> </xsl:template> </xsl:stylesheet> 

Command line (set inc2 to true)

 java -cp "saxon9he.jar" net.sf.saxon.Transform -s:"test.xml" -xsl:"test_main.xsl" inc2="true" 

Exit

 <doc> <foo>INCLUDE FILE 2!!!</foo> </doc> 
0
source

There is one approach I discovered to conditionally include files using the use-when function in XSLT 2.0.

You need to create one system property that is passed through the application at runtime, for example, in my case I use Java, so I created the Debug property using the command to launch my application:

 java -DDebug=yes myAppPath 

then in XSLT this property is used to include conditional files. for instance

 <xsl:include href="file1.xslt" use-when="system-property('DEBUG') = 'yes'"/> <xsl:include href="file2.xslt" use-when="system-property('DEBUG') != 'yes'"/> 

.

I don’t know if it is suitable for a given scenario, but YES there is a way to do this in XSLT 2.0. XSLT 3.0 also adds support for a local variable. Thus, this can be easily done using local variables.

Thank you, happy coding

0
source

Source: https://habr.com/ru/post/906776/


All Articles