从JTextArea中的xsl文件打印'<xsl:message>'</xsl:message>

时间:2015-01-07 11:04:54

标签: java xml xslt

我总是试图在JTextArea中打印我的xsl消息。使用我的上一个问题的代码,我可以在TextArea中打印xsl输出,但不能在xslt文件中写入消息。

Java代码:

public static void xslTransform(File xmlFile)throws IOException, TransformerException{
File xslFile = ...;
StreamSource xmlSource = new StreamSource(xmlFile);
StreamSource xslSource = new StreamSource(xslFile);
StreamResult result = new StreamResult (new StringWriter()); //maybe here is the problem?
TransformerFactory transformerFact = TransformerFactory.newInstance();
transformerFact.setAttribute(FeatureKeys.MESSAGE_EMITTER_CLASS, "MyMessageEmitter");
Transformer transformer = transformerFact.newTransformer(xslSource);
transformer.transform(xmlSource,result);
}


public class MyMessageEmitter extends net.sf.saxon.serialize.MessageEmitter{
String message;
private StringWriter stwriter = new StringWriter();
public void MyMessageEmitter() throws XPathException{
setWriter(stwriter);
}
@Override
public void close() throws XPathException{
super.close();
message=stwriter.toString();
myJTextArea.setText(message);
stwriter = new StringWriter();
}
}

XSLT文件:

<xsl:template match="/">
<xsl:for-each select="//@id">
<xsl:message>
<xsl:value-of select="name(parent::*)"/> <xsl:text></xsl:text><xsl:value-of select="."/>
</xslmessage>
</xsl:for-each>
</xsl:tempate>

来自java的这段代码消息是空的,因为这个xslt不粘贴任何输出(例如一个新的xml文档。我用另一个打印新xml的xslt检查了这个。

那么如何才能获得打印xslt的消息?

感谢您的帮助 荷风

2 个答案:

答案 0 :(得分:2)

如果MyMessageEmitter有对myJTextArea的引用,那么可能它是一个内部类。

这意味着它的全名不是“MyMessageEmitter”,而是更复杂的东西。使用您编写的代码,我得到了这个:

错误   无法加载MyMessageEmitter net.sf.saxon.trans.XPathException:无法加载MyMessageEmitter     在net.sf.saxon.trans.DynamicLoader.getClass(DynamicLoader.java:123)     ... 引起:java.lang.ClassNotFoundException:MyMessageEmitter

如果我将factory.setAttribute中的类名更改为正确的名称,我会得到:

错误   无法实例化类jaxptest.TransformMessageTest $ MyMessageEmitter net.sf.saxon.trans.XPathException:无法实例化类jaxptest.TransformMessageTest $ MyMessageEmitter     在net.sf.saxon.trans.DynamicLoader.getInstance(DynamicLoader.java:185)     ... 引起:java.lang.InstantiationException:jaxptest.TransformMessageTest $ MyMessageEmitter

这是因为除了包含它的类之外,不能实例化非静态内部类。

你的下一个错误是非常微妙的,我花了很长时间来诊断。你写过了

public void MyMessageEmitter() throws XPathException{ 
  setWriter(stwriter);
}

但是这使得MyMessageEmitter()成为普通方法,而你打算将它作为构造函数。所以它应该写成:

public MyMessageEmitter() throws XPathException{ 
  setWriter(stwriter);
}

如果我使内部类静态,它是有效的;但现在的问题是静态类不能简单地通过名称引用jTextArea。

下一个问题也非常微妙,这次撒克逊至少要责怪一半:MessageEmitter上的close()方法被调用两次。不幸的是,当发生这种情况时,第一次调用会创建一个新的空StringWriter,第二次调用会将此空StringWriter的内容写入您的文本区域。所以我改变了它在open()方法而不是close()方法中创建一个新的StringWriter。

这就留下了如何在MessageEmitter和JTextArea之间进行通信的问题。这不是微不足道的。我通过让MessageEmitter写入静态变量并让callling应用程序获取此静态变量来完成它。但显然,在生产应用程序中使用全局静态变量并不是真的可以接受。问题是Saxon创建了一个MessageEmitter类的实例,但没有提供与它通信的任何直接方式。我能找到的唯一解决方案是使用一些较低级别的Saxon接口,如下所示:

public void testMessageCapture() {
        try {
            String stylesheet =
                "<?xml version='1.0'?>" +
                    "<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform'" +
                    "    version='2.0'>" +
                    "  <xsl:template match='/'>" +
                    "<first/>" +
                    "<xsl:message>Hi!</xsl:message>" +
                    "  </xsl:template>" +
                    "</xsl:stylesheet>";

            TransformerFactory transFactory = new TransformerFactoryImpl();
            //transFactory.setAttribute(FeatureKeys.MESSAGE_EMITTER_CLASS, "jaxptest.TransformMessageTest$MyMessageEmitter");

            Templates templates = transFactory.newTemplates(
                new SAXSource(new InputSource(new StringReader(stylesheet))));


            StreamResult result = new StreamResult(new StringWriter());
            final StringWriter messageOut = new StringWriter();
            Transformer transformer = templates.newTransformer();
            ((net.sf.saxon.jaxp.TransformerImpl)transformer).getUnderlyingController().setMessageEmitter(
                new MessageEmitter() {
                    @Override
                    public void open() throws XPathException {
                        setWriter(messageOut);
                        super.open();
                    }
                }
            );
            transformer.transform(new StreamSource(new StringReader("<in/>")), result);
            assertEquals("Hi!", messageOut.toString().trim());
        } catch (TransformerException e) {
            e.printStackTrace();
            fail();
        }
    }

(当然你可以用直接写入JTextArea的方式将写作替换为字符串编写器。)

此代码实际上特定于Saxon 9.6;在早期版本中,您可以直接将JAXP Transformer转换为Controller。

这反过来说明了依赖JAXP接口的局限性。使用Saxon的原生s9api界面,这样做会容易得多。

答案 1 :(得分:1)

我明白了......这是解决方案:

Processor proc = new Processor(false);
XsltCompiler comp = new proc.newXsltCompiler();
StreamSource styleSource = new StreamSource(xsltFile);
StreamSource xmlSource = new StreamSource(xmlFile);
String msg="";
XsltExecutable templates = comp.compile(styleSource);
XsltTransformer transformer = templates.load();
transformer.setSource(xmlSource);
transformer.setMessageListener(new MessageListener(){
@Override
public void message (XdmNode content, boolean terminate, SourceLocator locator){
try{
if (content.getTypedValue!= null){
msg += content.getTypedValue().toString() + "\n";
}}
catch (SaxonApiException ex){
Logger.getLogger(...);
}
}
});
Writer write = new StringWriter();
Serializer out = proc.newSerializer(write);
transformer.setDestination(out);
transformer.transform();
textAreaOut.setText(msg);

感谢您的帮助!