Saxon cannot find function: current group
I am trying to use Saxon with XSLT stylesheets and use the code examples in the XSLT2 specification ( 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 am 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:
@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 feature missing in the version of Saxon I'm using, or am I doing something wrong?
source to share
JAXP Strikes Again! The problem is that you are not actually using the Saxon version.
When you do this:
factory = net.sf.saxon.TransformerFactoryImpl.newInstance();
really looks like you are calling a Saxon method, doesn't it? But in Java static methods cannot be overridden this way (if I could ...). You simply call the newInstance () method in the base class, which looks for the path to the first XSLT processor it finds around. If you want to explicitly call Saxon, it is much better to avoid looking up the classpath by doing
factory = new net.sf.saxon.TransformerFactoryImpl();
source to share
Now I have found something that works.
@Test
public void testSaxonXslt2GroupTest1() throws Exception {
// http://stackoverflow.com/questions/9925483/calling-java-from-xsl-saxon
File xml_file = Fixtures.XSLT2_TEST1_XML;
File xsl_file = Fixtures.XSLT2_TEST1_XSL;
LOG.debug(FileUtils.readFileToString(xsl_file));
// replacing the fully qualified class name with the properties approach seems to work
System.setProperty("javax.xml.transform.TransformerFactory",
"net.sf.saxon.TransformerFactoryImpl");
TransformerFactory tfactory = TransformerFactory.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();
}
}
I thought using the exact constructor would be enough, but it doesn't seem to be the case.
source to share