How to support returning ArrayList in ExtensionFun

2019-08-25 04:27发布

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.

标签: java xslt saxon
1条回答
Deceive 欺骗
2楼-- · 2019-08-25 04:51

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:

public SequenceType getResultType() {
    return SequenceType.makeSequenceType(ItemType.ANY_MAP, OccurrenceIndicator.ZERO_OR_MORE);
}

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 the XdmMap.

(a) You could build a Java HashMap<String, String> and then convert it using the static method XdmMap.makeMap()

(b) You could construct the map incrementally:

XdmMap map = new XdmMap();
map = map.put(new XdmAtomicValue("name"), new XdmAtomicValue("John Doe"));
map = map.put(new XdmAtomicValue("age"), new XdmAtomicValue("24"));

etc. Note that XdmMap is immutable so each put() operation creates a new XdmMap instance; the original is unchanged.

Finally, you need to construct a sequence of these XdmMap instances. The simplest way is to construct a Java List<XdmMap> containing all the maps, and then convert this to an XdmValue using new XdmValue(Iterable<...> items).

查看更多
登录 后发表回答