3

I'm trying my best to learn how to use XSLT 1.0 from java code but am starting to loose my mind bit by bit slowly over time.

I have Xalan-J (both xalan.jar and xsltc.jar with all dependency jars), saxon and jing (the latter two are used elswhere in my project) on my classpath and am using JAXP with a combination of System.setProperty calls to force usage of class implementations such as TransformerFactory the way I want. This also enables me to see how different XSLT processors behave using a single XSL transformation file by simply changing the properties from code. In addition I also use xsltproc from a cygwin environment to see what it does too. I also debug in Oxygen to make sure my java code is not the problem.

Take a look at this dummy XSL:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"                
                xmlns:rng="http://relaxng.org/ns/structure/1.0"
                xmlns:ex="http://example.com/ns/ex"
                version="1.0">

  <xsl:output method="xml" encoding="utf-8"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="/">
    <!-- Start by finding the root grammar element -->
    <xsl:apply-templates select="rng:grammar"/>
  </xsl:template>

  <xsl:template match="/rng:grammar">
    <!-- Continue with child grammar elements -->
    <xsl:apply-templates select="descendant::rng:grammar"/>
  </xsl:template>

  <xsl:template match="rng:grammar">
    <!-- set the variable and pass it's value to another template -->
    <xsl:variable name="parameter" select="'any-string-value'"/>
    <xsl:message>initial value: <xsl:value-of select="$parameter"/></xsl:message>
    <xsl:apply-templates select="descendant::ex:data">
      <xsl:with-param name="parameter" select="$parameter"/>
    </xsl:apply-templates>
  </xsl:template>

  <xsl:template match="rng:optional">
    <!-- use the param value here -->
    <xsl:param name="parameter"/>
    <xsl:message>final value: <xsl:value-of select="$parameter"/></xsl:message>
  </xsl:template>

</xsl:stylesheet>

It does nothing except calling some templates that work on this dummy XML instance document:

<?xml version="1.0" encoding="UTF-8"?>
<grammar
   xmlns="http://relaxng.org/ns/structure/1.0"
   xmlns:ex="http://example.com/ns/ex"
   datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
  <start>
    <grammar ex:subgram="something" ns="http://example.com/ns/something">
      <start>
        <ex:data>
          <optional />
        </ex:data>
      </start>
    </grammar>
  </start>
</grammar>

I don't know what I'm doing wrong but passing the value of the only variable in that XSL works only with xsltc and xsltproc. Both normal xalan and saxon fail to pass the value. See results below.

xsltproc:

$ xsltproc transformation.xsl instance.xml
initial value: any-string-value
final value: any-string-value

Xalan Compiled processor (xsltc and also with java 1.6 default implementation):

Implementation: org.apache.xalan.xsltc.trax.TransformerFactoryImpl loaded from: ./lib/xalan-j_2_7_1/xsltc.jar
initial value: any-string-value
final value: any-string-value

Xalan Interpretive processor (xalan):

Implementation: org.apache.xalan.processor.TransformerFactoryImpl loaded from: ./lib/xalan-j_2_7_1/xalan.jar
initial value: any-string-value
final value:

saxon:

Implementation: com.icl.saxon.TransformerFactoryImpl loaded from: ./lib/saxon6-5-5/saxon.jar
initial value: any-string-value
final value:

I need this to work with xalan (reason being acceptable EXSLT support). I have no idea what could possibly be the reason for different outputs. I tought all of these processors implement XSLT 1.0 but I guess they do it differently. Is what I'm trying to do in my XSL not XSLT 1.0 compliant? Is there a workaround I could use to get this to work?

predi
  • 5,528
  • 32
  • 60

1 Answers1

2

The explanation is simple:

  <xsl:template match="rng:optional">
    <!-- use the param value here -->
    <xsl:param name="parameter"/>
    <xsl:message>final value: <xsl:value-of select="$parameter"/></xsl:message>
  </xsl:template>

rng:optional is a child of ex:data.

There is no template matchin ex:data, therefore it is processed by the XSLT built-in template for an element node.

The built-in template, of course, doesn't know anything about parameters being passed to it, so it doesn't pass them through to the next templates that it applies.

Thus, the template matching rng:optional (above) is selected for execution by the XSLT built-in template for elements and no parameters are passed to this selected template.

Therefore, the value of the parameter param is the empty string and you get the correct output from both Saxon and XalanJ.

Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431
  • That actually makes sense. Is seems that xsltproc and xsltc built-in templates handle parameters even though they aren't supposed to...making a what you believe to be a portable XSL into an exact opposite. – predi Feb 09 '12 at 14:58
  • @predi: So, the solution is to have a template matching `ex:data` that has the parameter declared and will pass it via its `xsl:apply-templates` – Dimitre Novatchev Feb 09 '12 at 15:30
  • yes. If you add the missing template, the parameter has the value one would expect it to have (within that template). It then has to be properly delegated through the entire "path" between your starting point and your target even if the in-between part is useless to you. Cutting corners with a descendant axis or something like it is a no-no if parameters are being used. – predi Feb 09 '12 at 17:18
  • In XSLT 2.0 and 2.0 + one can make use of a feature called "tunnel parameters". – Dimitre Novatchev Feb 09 '12 at 18:09