INTRODUCTION
- Saxon is a powerful XSLT and XQuery Processor.
- Saxon has 3 versions.
- 1) HE-Home Edition,
- 2) PE-Professional Edition and
- 3) EE-Enterprise Edition.
- Saxon-HE version is an open source. Libraries can be downloaded from https://sourceforge.net/projects/saxon/files/Saxon-HE/ or online maven repository https://mvnrepository.com/artifact/net.sf.saxon/Saxon-HE/
- Saxon-PE & EE versions are licensed.
SOFTWARES & TOOLS
- Eclipse IDE (Mars) with Maven Plugin
- Java 8
- Saxon-HE-9.7.0-18
EXAMPLE
Saxon has many inbuilt functions but after 9.2 version, those functions are moved to licensed versions PE & HE. We need to have proper license file to use the inbuilt functions.
In this post, we are going to learn how to write custom functions using Saxon-HE. These custom functions also called integrated extension functions.
Follow the below steps to calculate & display age using the date of birth of the employees.
Step 1
Create a Maven Project in eclipse and copy paste the below code into pom.xml dependency.
<dependency>
<groupId>net.sf.saxon</groupId>
<artifactId>Saxon-HE</artifactId>
<version>9.7.0-18</version>
</dependency>
|
Step 2
Create a Java Class AgeCalculator.java which extends ExtensionFunctionDefinition.java as below.
package jbr.saxon;
import java.time.LocalDate;
import java.time.Period;
import net.sf.saxon.expr.XPathContext;
import net.sf.saxon.lib.ExtensionFunctionCall;
import net.sf.saxon.lib.ExtensionFunctionDefinition;
import net.sf.saxon.om.Sequence;
import net.sf.saxon.om.StructuredQName;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.value.SequenceType;
import net.sf.saxon.value.StringValue;
public class AgeCalculator extends ExtensionFunctionDefinition {
@Override
public SequenceType[] getArgumentTypes() {
return new SequenceType[] { SequenceType.SINGLE_STRING };
}
@Override
public StructuredQName getFunctionQName() {
return new StructuredQName("emp", "http://example.com/saxon-extension", "ageCalc");
}
@Override
public SequenceType getResultType(SequenceType[] arg0) {
return SequenceType.SINGLE_STRING;
}
@Override
public ExtensionFunctionCall makeCallExpression() {
return new ExtensionFunctionCall() {
@Override
public Sequence call(XPathContext ctx, Sequence[] args) throws XPathException {
String output = null;
String[] input = args[0].iterate().next().getStringValue().split("-");
int year = Integer.valueOf(input[0]);
int month = Integer.valueOf(input[1]);
int dayOfMonth = Integer.valueOf(input[2]);
LocalDate dob = LocalDate.of(year, month, dayOfMonth);
output = String.valueOf(Period.between(dob, LocalDate.now()).getYears());
return StringValue.makeStringValue(output);
}
};
}
}
|
Note:
- Mention no. of arguments and its types at getArgumentTypes() method
- Define the output type at getResultType() method
- I have written inner class in makeCallExpression() method for my simple requirement, you can create separate class and extend ExtensionFunctionCall.java
- http://example.com/saxon-extension - you can give any url.
- Also, you can choose your own name for emp & ageCalc
Step 3
Create a Java class AgeCalculatorMain.java to test the custom function as below.
package jbr.saxon;
import java.io.File;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import net.sf.saxon.Configuration;
import net.sf.saxon.TransformerFactoryImpl;
import net.sf.saxon.trans.XPathException;
public class AgeCalculatorMain {
public static void main(String[] args) throws XPathException {
// Set saxon as your transformer.
System.setProperty("javax.xml.transform.TransformerFactory", "net.sf.saxon.TransformerFactoryImpl");
// specify input and output
transform("testdata/input/employees.xml", "testdata/input/employees.xsl", "testdata/output/output.html");
}
public static void transform(String sourcePath, String xsltPath, String resultDir) {
try {
TransformerFactory factory = TransformerFactory.newInstance();
if (factory instanceof TransformerFactoryImpl) {
TransformerFactoryImpl tFactoryImpl = (TransformerFactoryImpl) factory;
Configuration config = tFactoryImpl.getConfiguration();
config.registerExtensionFunction(new AgeCalculator());
}
Transformer transformer = factory.newTransformer(new StreamSource(new File(xsltPath)));
transformer.transform(new StreamSource(new File(sourcePath)), new StreamResult(new File(resultDir)));
System.out.println("Output generated successfully at: " + resultDir);
} catch (TransformerException e) {
e.printStackTrace();
}
}
}
|
Step 4
Create an input xml file (employees.xml)
<?xml version="1.0" encoding="UTF-8"?>
<employees>
<employee>
<name>Anbu</name>
<address>Chennai</address>
<dob>1990-05-15</dob>
<phone>9600096000</phone>
<email>anbu@email.com</email>
</employee>
<employee>
<name>Bala</name>
<address>Hydrabad</address>
<dob>1989-01-04</dob>
<phone>9700097000</phone>
<email>bala@email.com</email>
</employee>
<employee>
<name>Chandru</name>
<address>Mumbai</address>
<dob>1995-11-10</dob>
<phone>9900099000</phone>
<email>chandru@email.com</email>
</employee>
</employees>
|
Step 5
Create an XSLT file (employees.xsl). Define the custom function and call it.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:emp="http://example.com/saxon-extension">
<xsl:template match="/">
<html>
<body>
<h2>Employee Details</h2>
<table border="1">
<tr bgcolor="#9acd32">
<th style="text-align:left">Name</th>
<th style="text-align:left">Address</th>
<th style="text-align:left">Date of Birth</th>
<th style="text-align:left">Age</th>
<th style="text-align:left">Phone</th>
<th style="text-align:left">Email</th>
</tr>
<xsl:for-each select="employees/employee">
<tr>
<td>
<xsl:value-of select="name" />
</td>
<td>
<xsl:value-of select="address" />
</td>
<td>
<xsl:value-of select="dob" />
</td>
<td>
<xsl:value-of select="emp:ageCalc(dob)" />
</td>
<td>
<xsl:value-of select="phone" />
</td>
<td>
<xsl:value-of select="email" />
</td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
|
OUTPUT
Now Run the AgeCalculatorMain.java and the output html file will be copied to /testdata/output/output.html
Open the output.html in any browser.
That’s all about writing a custom function using Saxon HE version. Please share your comments.
Happy Knowledge Sharing!!!