我总是试图在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接口来实现这一点要容易得多。