Arrays with java XSLT Extensions

2019-05-24 08:06发布

I'm trying to use arrays in XSLT Extensions using java .

I'm getting the below error :

Caused by: java.lang.ClassCastException: org.apache.xpath.objects.XObject 
                     cannot be cast to org.apache.xpath.objects.XNodeSet.

The way I used arrays is. An extension class method

public static String[] getEvents(String contractIdStr,String tradeIdStr) {
    return new String[]{"MacroType","Type","SubType"};
}

Inside XSL ,

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:partyrefrule="com.converter.Rules" 
 exclude-result-prefixes="partyrefrule">

    <xsl:variable name="vLastNegoTradeEvents">
        <xsl:value-of select="partyrefrule:getEvents($cVal,$tVal)"/>
    </xsl:variable>

    <xsl:message terminate="no">
        <xsl:value-of select="$vLastNegoTradeEvents[0]"/>
    </xsl:message>
</xsl:stylesheet>

I'm using XALAN Parse for this.

标签: java xslt
1条回答
Emotional °昔
2楼-- · 2019-05-24 08:39

I don't think you can have an XPath function returning an array of string. What you can create however is an XPath XALAN extention that returns a NodeSet. In your case, you probably want a node set containing text nodes. Then, all you have to do is loop on all the nodes of your node set to retrieve all the strings you have generated in your extension node-set.
I have refactored a little bit you example to illustrate what I believe a solution as close as possible to what you want should look like. The first class is your extension. As I said, it creates a node set rather than array of strings. The second class has the XSL. It's a little bit cryptic because I needed to have everything (compact) in java in order to use eclipse java debugger (mixing eclipse XSLT and java debuggers is a non starter).

So here you go: first the extension:

package com.converter;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;

import org.apache.xalan.extensions.ExpressionContext;
import org.apache.xpath.NodeSet;
import org.apache.xpath.objects.XNodeSet;
import org.apache.xpath.objects.XNodeSetForDOM;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;

public class Rules {


    public static XNodeSet getEvents(ExpressionContext context, String s1, String s2) throws TransformerException {

        XNodeSet result = null ;

        try {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance() ;
            DocumentBuilder dBuilder;
            dBuilder = dbf.newDocumentBuilder();
            Document doc = dBuilder.newDocument();

            NodeSet ns = new NodeSet();

            ns.addNode( doc.createTextNode("MacroType" + s1 ) ) ;
            ns.addNode( doc.createTextNode("Type" + s2 ) ) ;
            ns.addNode( doc.createTextNode("SubType" + s1 + s2 ) ) ;

            result = new XNodeSetForDOM( (NodeList)ns, context.getXPathContext() );

        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        }
        return result ;
    }

}

Then the test class (with the embedded XSL).

import java.io.StringReader;

import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

public class Test {

    private static final String TESTXSL = "<?xml version=\"1.0\" encoding=\"UTF-8\"?> " +
        "<xsl:stylesheet version=\"1.0\"  " +
        " xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" " +
        " xmlns:partyrefrule=\"com.converter.Rules\" " +
        " exclude-result-prefixes=\"partyrefrule\"> " +
        "    <xsl:template match=\"/\"> " +
        "        <xsl:for-each select=\"partyrefrule:getEvents( 's1', 's2' )\"> " +
        "            <xsl:value-of select=\".\"/> " +
        "            <xsl:if test=\"position() != last()\">,</xsl:if> " +
        "        </xsl:for-each></xsl:template> " +
        "</xsl:stylesheet>" ;

    public static void main(String[] args) throws Exception {
        new Test().run () ;
    }

    public void run () throws Exception {
        TransformerFactory transFact = TransformerFactory.newInstance( );
        Source xsltSource = new StreamSource( new StringReader( TESTXSL ) );
        Transformer xsl = transFact.newTransformer(xsltSource);
        Source src = new DOMSource () ; // unused anyway
        Result result = new StreamResult( System.out ) ;
        xsl.transform(src , result );
    }

}

With these 2 classes, the output is

<?xml version="1.0" encoding="UTF-8"?>MacroTypes1,Types2,SubTypes1s2

Again, as the other XSLT VIPs of this thread have pointed out, this solution will tie you up to Xalan Java version 2.6+... But the truth of the matter is that:
1. XSLT 1.0 is often useless without extensions.
2. each processor has its own way (javascript for MSXML, java for Xalan, declared entry points for Xalan-C...)

查看更多
登录 后发表回答