我总是试图在jtextArea中打印xsl消息。使用最后一个问题的代码,我可以在文本区域中打印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文档),因此来自Java的此代码消息是空的。我用另一个打印新xml的xslt对此进行了检查。
那么,如何获取打印xslt的消息呢?
谢谢你的帮助
卡夫

最佳答案

如果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。
下一个问题也相当微妙,这一次至少有一半要归咎于saxon:MessageEmitter上的close()方法被调用了两次。不幸的是,当发生这种情况时,第一个调用将创建一个新的空stringwriter,而第二个调用将此空stringwriter的内容写入您的文本区域。所以我修改了它,在open()方法而不是close()方法中创建了一个新的stringwriter。
现在剩下的问题是如何在MessageEmitter和JTextArea之间进行通信。这不是小事。我是通过让MessageEmitter写入一个静态变量,并让调用应用程序获取这个静态变量来实现的。但是很明显,在生产应用程序中使用全局静态变量是不可接受的。问题在于,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转换器直接转换为控制器。
这恰恰说明了依赖jaxp接口的局限性。使用saxon的本地s9api接口来实现这一点要容易得多。

09-26 15:24