Search code examples
xmlmavenxslt-2.0saxonxslt-grouping

Saxon cannot find function: current-group


I am trying to use Saxon with XSLT stylesheets and using the code examples in the XSLT2 spec (http://www.w3.org/TR/xslt20/#xsl-for-each-group)

<table xsl:version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <tr>
    <th>Position</th>
    <th>Country</th>
    <th>City List</th>
    <th>Population</th>
  </tr>
  <xsl:for-each-group select="cities/city" group-by="@country">
    <tr>
      <td><xsl:value-of select="position()"/></td>
      <td><xsl:value-of select="@country"/></td>
      <td>
        <xsl:value-of select="current-group()/@name" separator=", "/>
      </td>
      <td><xsl:value-of select="sum(current-group()/@pop)"/></td>
    </tr>
  </xsl:for-each-group>
</table>

I'm using the following in my pom.xml

<dependency>
  <groupId>net.sf.saxon</groupId>
  <artifactId>Saxon-HE</artifactId>
  <version>9.6.0-3</version>
</dependency>   

and the code to run it is:

    @Test
    public void testSaxonXslt2GroupTest1() throws Exception {

        File xml_file = Fixtures.XSLT2_TEST1_XML;
        File xsl_file = Fixtures.XSLT2_TEST1_XSL;


        TransformerFactory tfactory = net.sf.saxon.TransformerFactoryImpl.newInstance();
        Transformer transformer = tfactory.newTransformer(new StreamSource(xsl_file));
        File saxonDir = new File("target/saxon/");
        saxonDir.mkdirs();
        try {
            transformer.transform(new StreamSource(xml_file),  
                new StreamResult(new FileOutputStream(new File(saxonDir, "test1.xml"))));
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }

This throws an error on the output console

SystemId Unknown; Line #13; Column #70; Could not find function: current-group
SystemId Unknown; Line #13; Column #70; function token not found.
(Location of error unknown)java.lang.NullPointerException

Is this function missing in the Saxon version I am using, or am I doing something wrong?


Solution

  • JAXP strikes again! The problem is, you are not actually running Saxon.

    When you do this:

    factory = net.sf.saxon.TransformerFactoryImpl.newInstance();
    

    it really looks as if you are calling a Saxon method, doesn't it? But in Java, static methods can't be overridden in this way (I would if I could...). You are simply calling the newInstance() method on the base class, which searches the classpath for the first XSLT processor it finds lying around. If you want to invoke Saxon explicitly, it's much better to avoid the classpath search by doing

    factory = new net.sf.saxon.TransformerFactoryImpl();