从 JTextArea 中的 xsl 文件打印“<xsl:message>”
Printing '<xsl:message>' from a xsl file in JTextArea
我总是试图在 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 的消息呢?
感谢大家的帮助
卡夫
如果 MyMessageEmitter 引用了 myJTextArea,那么它可能是一个内部 class.
这意味着它的全名不是"MyMessageEmitter"而是更复杂的东西。使用您编写的代码,我得到了这个:
错误
无法加载 MyMessageEmitter
net.sf.saxon.trans.XPathException: 未能加载 MyMessageEmitter
在 net.sf.saxon.trans.DynamicLoader.getClass(DynamicLoader.java:123)
...
由以下原因引起:java.lang.ClassNotFoundException:MyMessageEmitter
如果我将 factory.setAttribute 中的 class 名称更改为正确的名称,我会得到:
错误
无法实例化 class jaxptest.TransformMessageTest$MyMessageEmitter
net.sf.saxon.trans.XPathException: 无法实例化 class jaxptest.TransformMessageTest$MyMessageEmitter
在 net.sf.saxon.trans.DynamicLoader.getInstance(DynamicLoader.java:185)
...
原因:java.lang.InstantiationException:jaxptest.TransformMessageTest$MyMessageEmitter
这是因为非静态内部 class 无法实例化,除非它包含 class。
您的下一个错误非常微妙,我花了很长时间才诊断出来。你写了
public void MyMessageEmitter() throws XPathException{
setWriter(stwriter);
}
但这使 MyMessageEmitter() 成为普通方法,而您希望它成为构造函数。所以应该写成:
public MyMessageEmitter() throws XPathException{
setWriter(stwriter);
}
如果我将内部 class 设置为静态,它就会工作;但现在的问题是静态 class 不能简单地按名称引用 jTextArea。
下一个问题也很微妙,这次至少有一半是 Saxon 的责任:MessageEmitter 的 close() 方法被调用了两次。不幸的是,当发生这种情况时,第一次调用创建了一个新的空 StringWriter,第二次调用将这个空 StringWriter 的内容写入您的文本区域。所以我将其更改为在 open() 方法中而不是在 close() 方法中创建一个新的 StringWriter。
现在留下了如何在 MessageEmitter 和 JTextArea 之间进行通信的问题。这不是微不足道的。我通过让 MessageEmitter 写入一个静态变量,并让调用应用程序获取这个静态变量来做到这一点。但显然,在生产应用程序中使用全局静态变量是不可接受的。问题在于 Saxon 创建了 MessageEmitter class 的一个实例,但不提供任何与其通信的直接方式。我能找到的唯一解决方案涉及使用一些较低级别的 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 接口,这样做会容易得多。
我明白了...这是解决方案:
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);
感谢您的帮助!
我总是试图在 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 的消息呢?
感谢大家的帮助 卡夫
如果 MyMessageEmitter 引用了 myJTextArea,那么它可能是一个内部 class.
这意味着它的全名不是"MyMessageEmitter"而是更复杂的东西。使用您编写的代码,我得到了这个:
错误 无法加载 MyMessageEmitter net.sf.saxon.trans.XPathException: 未能加载 MyMessageEmitter 在 net.sf.saxon.trans.DynamicLoader.getClass(DynamicLoader.java:123) ... 由以下原因引起:java.lang.ClassNotFoundException:MyMessageEmitter
如果我将 factory.setAttribute 中的 class 名称更改为正确的名称,我会得到:
错误 无法实例化 class jaxptest.TransformMessageTest$MyMessageEmitter net.sf.saxon.trans.XPathException: 无法实例化 class jaxptest.TransformMessageTest$MyMessageEmitter 在 net.sf.saxon.trans.DynamicLoader.getInstance(DynamicLoader.java:185) ... 原因:java.lang.InstantiationException:jaxptest.TransformMessageTest$MyMessageEmitter
这是因为非静态内部 class 无法实例化,除非它包含 class。
您的下一个错误非常微妙,我花了很长时间才诊断出来。你写了
public void MyMessageEmitter() throws XPathException{
setWriter(stwriter);
}
但这使 MyMessageEmitter() 成为普通方法,而您希望它成为构造函数。所以应该写成:
public MyMessageEmitter() throws XPathException{
setWriter(stwriter);
}
如果我将内部 class 设置为静态,它就会工作;但现在的问题是静态 class 不能简单地按名称引用 jTextArea。
下一个问题也很微妙,这次至少有一半是 Saxon 的责任:MessageEmitter 的 close() 方法被调用了两次。不幸的是,当发生这种情况时,第一次调用创建了一个新的空 StringWriter,第二次调用将这个空 StringWriter 的内容写入您的文本区域。所以我将其更改为在 open() 方法中而不是在 close() 方法中创建一个新的 StringWriter。
现在留下了如何在 MessageEmitter 和 JTextArea 之间进行通信的问题。这不是微不足道的。我通过让 MessageEmitter 写入一个静态变量,并让调用应用程序获取这个静态变量来做到这一点。但显然,在生产应用程序中使用全局静态变量是不可接受的。问题在于 Saxon 创建了 MessageEmitter class 的一个实例,但不提供任何与其通信的直接方式。我能找到的唯一解决方案涉及使用一些较低级别的 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 接口,这样做会容易得多。
我明白了...这是解决方案:
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);
感谢您的帮助!