I have a program that uses Saxon HE 9.7 for XML transformation.
public String transform() throws TransformerException {
TransformerFactory factory = TransformerFactory.newInstance();
TransformerFactoryImpl tFactoryImpl = (TransformerFactoryImpl) factory;
Configuration saxonConfig = tFactoryImpl.getConfiguration();
Processor processor = (Processor) saxonConfig.getProcessor();
processor.registerExtensionFunction(new Employee());
Source xslt = new StreamSource(new File("mappings.xslt"));
Transformer transformer = factory.newTransformer(xslt);
Source text = new StreamSource(new File("payload.xml"));
transformer.transform(text, new StreamResult(sw));
return sw.toString();
}
The ExtensionFunction class:
public class Employee implements ExtensionFunction {
private List<HashMap<String, String>> employee = new ArrayList<HashMap<String, String>>();
private String employeeName = "John";
public List<HashMap<String, String>> getEmployee() {
HashMap<String, String> map1 = new HashMap<>();
map1.put("name", "john");
HashMap<String, String> map2 = new HashMap<>();
map2.put("age", "30");
employee.add(map1);
employee.add(map2);
return employee;
}
public String getEmployeeName(){
return employeeName;
}
@Override
public XdmValue call(XdmValue[] arg0) throws SaxonApiException {
return new XdmAtomicValue(getEmployeeName());
}
@Override
public SequenceType[] getArgumentTypes() {
return new SequenceType[] {};
}
@Override
public QName getName() {
return new QName("test.extension.Employee", "getEmployeeName");
}
@Override
public SequenceType getResultType() {
return SequenceType.makeSequenceType(ItemType.STRING, OccurrenceIndicator.ONE);
}
XSLT File:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0" xmlns:Employee="test.extension.Employee"
xmlns:saxon="http://saxon.sf.net/">
<xsl:output method="xml" indent="yes" />
<xsl:variable name="list"
select="Employee.getEmployee()" />
<xsl:variable name="count" select="count($list)" />
<xsl:template match="/">
<company>
<employee>
<xsl:attribute name="name">
<xsl:value-of select="$list[1]" />
</xsl:attribute>
</employee>
</company>
</xsl:template>
</xsl:stylesheet>
Currently, I am able to call getEmployeeName method that returns a String from xslt. But how do I modify Employee class to also support returning ArrayList of HasMap i.e the method getEmployee() as the new XdmAtomicValue() method doesn't takes ArrayList as constructor argument.
I'm going to answer for Saxon 9.9, because that's the release you should really be using. The
XdmMap
class isn't present in Saxon 9.7, so passing maps between Java and XSLT with that release is going to be difficult or impossible.You first need to decide what XDM type you want to return: I assume this is probably
map(xs:string, xs:string)*
- that is a sequence of maps with strings as the key and strings as the value.You first need to declare this as the result type in your
getResultType()
method. It's probably good enough to do:That's not quite as precise as it might be, but supplying a more precise result type isn't going to achieve anything other than causing Saxon to do more careful (and expensive) checking of what the function actually returns. If you wanted to provide a more precise return type you would have to construct it using
ItemTypeFactory.newMapType(...)
.Then your
call()
method needs to return an instance of this type.You'll want to represent each Employee as an
XdmMap
. There are two ways you can construct theXdmMap
.(a) You could build a Java
HashMap<String, String>
and then convert it using the static methodXdmMap.makeMap()
(b) You could construct the map incrementally:
etc. Note that
XdmMap
is immutable so eachput()
operation creates a newXdmMap
instance; the original is unchanged.Finally, you need to construct a sequence of these
XdmMap
instances. The simplest way is to construct a JavaList<XdmMap>
containing all the maps, and then convert this to anXdmValue
usingnew XdmValue(Iterable<...> items)
.