Daniel Haley
Daniel Haley

Reputation: 52888

Using conditional includes/static parameters in Saxon/C?

I'm trying to use XSLT conditional includes/static parameters with Saxon/C HE, but I'm getting the following error:

Error 
  Parameter $some_param cannot be supplied dynamically because it is declared as static

To reproduce, I've used an example from a couple of answers I added a few years ago. (Here and here.)

In both of those answers I used the java version 9.7 of Saxon-HE from the command line without issue. I also tested again using java version 10.5 of HE from the command line. Again no issues.

However, if I try to run this example from Python (3.8) using Saxon/C 1.2.1 running with Saxon-HE 9.9.1.5C I get the error above.

Does anyone else have experience with static params in XSLT 3.0 and Saxon/C (especially with Python) that can provide guidance?

Test code...

XML Input (test.xml)

<doc>
    <foo/>
</doc>

Python

import saxonc

saxon_proc = saxonc.PySaxonProcessor(license=False)

print(f"Processor version: {saxon_proc.version}")

xsltproc = saxon_proc.new_xslt30_processor()

# noinspection PyArgumentList
xsltproc.set_parameter("inc2", saxon_proc.make_boolean_value(True))

results = xsltproc.transform_to_string(source_file="test.xml", stylesheet_file="test_main.xsl")

if results:
    print(results)

saxon_proc.release()

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

First possible included 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>

Second possible included 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>

Expected Output (this is what I get using java Saxon-HE from the command line (shown below))

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

Actual Output

Processor version: Saxon/C 1.2.1 running with Saxon-HE 9.9.1.5C from Saxonica
Error 
  Parameter $inc2 cannot be supplied dynamically because it is declared as static

Working command line:

java -cp "saxon-he-10.5.jar" net.sf.saxon.Transform -s:"test.xml" -xsl:"test_main2.xsl" inc2="true"

I should also note that I get the same error when trying to use a shadow attribute (command line still works using the command line arg inc_number="2"):

<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="inc_number" as="xs:string" select="'1'" static="yes" required="no"/>

    <xsl:include _href="test_inc{$inc_number}.xsl"/>

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    
</xsl:stylesheet>

Upvotes: 3

Views: 331

Answers (2)

Daniel Haley
Daniel Haley

Reputation: 52888

From @ond1 (O'Neil Delpratt - Saxonica)...

Hi, unfortunately it is not currently possible to set the static parameters in the current version, but I have added this feature which will be available in the next release.

So until setting static params is supported, I'm going to use two different "main" XSLTs; each one including the desired stylesheet. Another option is to transform the XSLT before executing it, but the two separate "main"'s is what I'm already doing anyway.

Also, once setting static params is supported I'll update this answer with a full working example.


UPDATE

Martin confirmed this was fixed in SaxonC 11.1. I was also able to test/confirm with SaxonC 12.

Here are updated Python examples that use the XSLT from my original question.

These were tested with SaxonC-HE 12 installed using pip from pypi.

Example #1

from saxonche import PySaxonProcessor

saxon_proc = PySaxonProcessor(license=False)

print(f"Processor version: {saxon_proc.version}")

xsltproc = saxon_proc.new_xslt30_processor()

xsltproc.set_parameter("inc2", saxon_proc.make_boolean_value(True))

results = xsltproc.transform_to_string(source_file="test.xml", stylesheet_file="test_main.xsl")

if results:
    print(results)

Example #2 (shadow attribute)

from saxonche import PySaxonProcessor

saxon_proc = PySaxonProcessor(license=False)

print(f"Processor version: {saxon_proc.version}")

xsltproc = saxon_proc.new_xslt30_processor()

xsltproc.set_parameter("inc_number", saxon_proc.make_string_value("2"))

results = xsltproc.transform_to_string(source_file="test.xml", stylesheet_file="test_main2.xsl")

if results:
    print(results)

I'm not using with to manage context in my examples, but the pypi documentation recommends it (could not find this mentioned in the Saxonica documentation).

Upvotes: 1

Martin Honnen
Martin Honnen

Reputation: 167716

My first test with SaxonC 11.1 and the above XSLT code and using the Python API results in the Python code

from saxonc import *

with PySaxonProcessor(license=False) as proc:
    print("Test SaxonC on Python")
    print(proc.version)
    
    xslt30proc = proc.new_xslt30_processor()
    
    result = xslt30proc.transform_to_string(stylesheet_file = 'main-sheet.xsl', source_file = 'test.xml')
         
    print(result)
    
    xslt30proc.set_parameter('inc1', proc.make_boolean_value(True))
    
    result = xslt30proc.transform_to_string(stylesheet_file = 'main-sheet.xsl', source_file = 'test.xml')
  
    print(result)
    
    xslt30proc.set_parameter('inc2', proc.make_boolean_value(True))
    
    result = xslt30proc.transform_to_string(stylesheet_file = 'main-sheet.xsl', source_file = 'test.xml')
  
    print(result)

and outputs

Test SaxonC on Python
SaxonC-HE 11.1 from Saxonica
source in transformFiletoString=test.xml stylsheet=main-sheet.xsl
<?xml version="1.0" encoding="UTF-8"?>
<doc>
   <foo/>
</doc>

source in transformFiletoString=test.xml stylsheet=main-sheet.xsl
<?xml version="1.0" encoding="UTF-8"?>
<doc>
   <foo>INCLUDE FILE 1!!!</foo>
</doc>

source in transformFiletoString=test.xml stylsheet=main-sheet.xsl
Warning at mode (unnamed)
  XTDE0540  Ambiguous rule match for /doc/foo[1]
Matches both "foo" on line 3 of
  file:/C:/Users/marti/OneDrive/Documents/xslt/blog-xslt-3-by-example/saxonc-11.1-python/conditional-include-with-static-params/test_inc2.xsl
and "foo" on line 3 of file:/C:/Users/marti/OneDrive/Documents/xslt/blog-xslt-3-by-example/saxonc-11.1-python/conditional-include-with-static-params/test_inc1.xsl
<?xml version="1.0" encoding="UTF-8"?>
<doc>
   <foo>INCLUDE FILE 2!!!</foo>
</doc>

Or just your example of setting the parameter and running the stylesheet:

from saxonc import *

with PySaxonProcessor(license=False) as proc:
    print("Test SaxonC on Python")
    print(proc.version)
    
    xslt30proc = proc.new_xslt30_processor()
    
    xslt30proc.set_parameter('inc2', proc.make_boolean_value(True))
    
    result = xslt30proc.transform_to_string(stylesheet_file = 'main-sheet.xsl', source_file = 'test.xml')
  
    print(result)

gives

Test SaxonC on Python
SaxonC-HE 11.1 from Saxonica
source in transformFiletoString=test.xml stylsheet=main-sheet.xsl
<?xml version="1.0" encoding="UTF-8"?>
<doc>
   <foo>INCLUDE FILE 2!!!</foo>
</doc>

Upvotes: 1

Related Questions